Merge remote-tracking branch 'upstream/master' into mrozentsvayg/cloudflare-custom-hostname

This commit is contained in:
Mikhail Rozentsvayg 2025-02-17 09:39:41 -08:00
commit 147df48ff5
29 changed files with 4809 additions and 308 deletions

View File

@ -18,6 +18,25 @@ jobs:
with:
fetch-depth: 0
- name: Install Helm
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
with:
token: ${{ github.token }}
version: latest
- name: Run Helm Schema check
working-directory: charts/external-dns
run: |
set -euo pipefail
helm plugin install https://github.com/losisin/helm-values-schema-json.git
helm schema
if [[ -n "$(git status --porcelain --untracked-files=no)" ]]
then
echo "Schema not up to date. Please run helm schema and commit changes!" >&2
exit 1
fi
- name: Install Helm Docs
uses: action-stars/install-tool-from-github-release@ece2623611b240002e0dd73a0d685505733122f6 # v0.2.4
with:
@ -52,12 +71,6 @@ jobs:
- name: Run Artifact Hub lint
run: ah lint --kind helm || exit 1
- name: Install Helm
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
with:
token: ${{ github.token }}
version: latest
- name: Install Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# cover-html creates coverage report for whole project excluding vendor and opens result in the default browser
#? cover: Creates coverage report for whole project excluding vendor and opens result in the default browser
.PHONY: cover cover-html
.DEFAULT_GOAL := build
@ -24,11 +24,11 @@ cover:
gocovmerge `ls *.coverprofile` > cover.out
rm *.coverprofile
#? cover-html: Run tests with coverage and open coverage report in the browser
cover-html: cover
go tool cover -html cover.out
# find or download controller-gen
# download controller-gen if necessary
#? controller-gen: download controller-gen if necessary
controller-gen:
ifeq (, $(shell which controller-gen))
@{ \
@ -40,15 +40,16 @@ else
CONTROLLER_GEN=$(shell which controller-gen)
endif
#? golangci-lint: Install golangci-lint tool
golangci-lint:
@command -v golangci-lint > /dev/null || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.63.4
# Run the golangci-lint tool
#? go-lint: Run the golangci-lint tool
.PHONY: go-lint
go-lint: golangci-lint
golangci-lint run --timeout=30m ./...
# Run the licensecheck script to check for license headers
#? licensecheck: Run the to check for license headers
.PHONY: licensecheck
licensecheck:
@echo ">> checking license header"
@ -60,25 +61,25 @@ licensecheck:
exit 1; \
fi
# Requires to install spectral. See https://github.com/stoplightio/spectral
#? oas-lint: Requires to install spectral. See github.com/stoplightio/spectral
oas-lint:
spectral lint api/*.yaml
# Run all the linters
#? lint: Run all the linters
.PHONY: lint
lint: licensecheck go-lint oas-lint
# generates CRD using controller-gen
#? crd: Generates CRD using controller-gen
.PHONY: crd
crd: controller-gen
${CONTROLLER_GEN} crd:crdVersions=v1 paths="./endpoint/..." output:crd:stdout > docs/contributing/crd-source/crd-manifest.yaml
# The verify target runs tasks similar to the CI tasks, but without code coverage
#? test: The verify target runs tasks similar to the CI tasks, but without code coverage
.PHONY: test
test:
go test -race -coverprofile=profile.cov ./...
# The build targets allow to build the binary and container image
#? build: The build targets allow to build the binary and container image
.PHONY: build
BINARY ?= external-dns
@ -148,9 +149,9 @@ clean:
@rm -rf build
@go clean -cache
# Builds and push container images to the staging bucket.
.PHONY: release.staging
.PHONY: release.staging
#? release.staging: Builds and push container images to the staging bucket.
release.staging: test
IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch
@ -161,17 +162,25 @@ release.prod: test
ko:
scripts/install-ko.sh
# generate-flags-documentation: Generate documentation (docs/flags.md)
.PHONY: generate-flags-documentation
#? generate-flags-documentation: Generate documentation (docs/flags.md)
generate-flags-documentation:
go run internal/gen/docs/flags/main.go
pre-commit-install: ## Install pre-commit hooks
#? pre-commit-install: Install pre-commit hooks
pre-commit-install:
@pre-commit install
@pre-commit gc
pre-commit-uninstall: ## Uninstall hooks
#? pre-commit-uninstall: Uninstall pre-commit hooks
pre-commit-uninstall:
@pre-commit uninstall
pre-commit-validate: ## Validate files with pre-commit hooks
#? pre-commit-validate: Validate files with pre-commit hooks
pre-commit-validate:
@pre-commit run --all-files
.PHONY: help
#? help: Get more info on available commands
help: Makefile
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'

View File

@ -21,3 +21,6 @@
.idea/
*.tmproj
.vscode/
ci/
schema/
.schema.yaml

View File

@ -0,0 +1,11 @@
# ref: https://github.com/losisin/helm-values-schema-json.git
input:
- schema/values.yaml
- values.yaml
draft: 7
indent: 2
output: values.schema.json
schemaRoot:
additionalProperties: true

View File

@ -18,6 +18,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED]
### Added
- Added ability to generate schema with `helm plugin schema`. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
- Added `docs/contributing/dev-guide.md#helm-values` guide. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
### Changed
- Regenerate JSON schema with `helm-values-schema-json' plugin. ([#5075](https://github.com/kubernetes-sigs/external-dns/pull/5075)) _@ivankatliarchuk_
## [v1.15.2] - 2025-02-14
### Changed
- Added `transportservers` resource to ClusterRole when specifying `f5-transportserver` or `f5-virtualserver` as a source. ([#5066](https://github.com/kubernetes-sigs/external-dns/pull/5066)) _@visokoo_
@ -25,8 +36,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixed handling of non-string types in `serviceAccount.metadata.annotations` field. ([#5067](https://github.com/kubernetes-sigs/external-dns/pull/5067)) _@hjoshi123_
- Fixed regression where `affinity.nodeAffinity` was being ignored. ([#5046](https://github.com/kubernetes-sigs/external-dns/pull/5046)) _@mkhpalm_
## [v1.15.1] - 2023-09-10
## [v1.15.1] - 2025-01-27
### Added
@ -225,6 +237,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
RELEASE LINKS
-->
[UNRELEASED]: https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns
[v1.15.2]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.2
[v1.15.1]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.1
[v1.15.0]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.15.0
[v1.14.5]: https://github.com/kubernetes-sigs/external-dns/releases/tag/external-dns-helm-chart-1.14.5

View File

@ -2,7 +2,7 @@ apiVersion: v2
name: external-dns
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
type: application
version: 1.15.1
version: 1.15.2
appVersion: 0.15.1
keywords:
- kubernetes
@ -21,14 +21,8 @@ maintainers:
annotations:
artifacthub.io/changes: |
- kind: added
description: "Added ability to configure `imagePullSecrets` via helm `global` value."
- kind: added
description: "Added options to configure `labelFilter` and `managedRecordTypes` via dedicated helm values."
- kind: changed
description: "Allow templating `serviceaccount.annotations` keys and values, by rendering them using the `tpl` built-in function."
- kind: changed
description: "Updated _ExternalDNS_ OCI image version to [v0.15.1](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.15.1)."
description: "Added `transportservers` resource to ClusterRole when specifying `f5-transportserver` or `f5-virtualserver` as a source."
- kind: fixed
description: "Fixed automatic addition of pod selector labels to `affinity` and `topologySpreadConstraints` if not defined."
description: "Fixed handling of non-string types in `serviceAccount.metadata.annotations` field."
- kind: fixed
description: "Fixed missing Ingress permissions when using Istio sources."
description: "Fixed regression where `affinity.nodeAffinity` was being ignored."

View File

@ -1,6 +1,6 @@
# external-dns
![Version: 1.15.1](https://img.shields.io/badge/Version-1.15.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.15.1](https://img.shields.io/badge/AppVersion-0.15.1-informational?style=flat-square)
![Version: 1.15.2](https://img.shields.io/badge/Version-1.15.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.15.1](https://img.shields.io/badge/AppVersion-0.15.1-informational?style=flat-square)
ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
@ -27,7 +27,7 @@ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
After you've installed the repo you can install the chart.
```shell
helm upgrade --install external-dns external-dns/external-dns --version 1.15.1
helm upgrade --install external-dns external-dns/external-dns --version 1.15.2
```
## Providers
@ -151,7 +151,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| secretConfiguration.subPath | string | `nil` | Sub-path for mounting the `Secret`, this can be templated. |
| securityContext | object | See _values.yaml_ | [Security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `external-dns` container. |
| service.annotations | object | `{}` | Service annotations. |
| service.ipFamilies | list | `[]` | Service IP families. |
| service.ipFamilies | list | `[]` | Service IP families (e.g. IPv4 and/or IPv6). |
| service.ipFamilyPolicy | string | `nil` | Service IP family policy. |
| service.port | int | `7979` | Service HTTP port. |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. Templates are allowed in both the key and the value. Example: `example.com/annotation/{{ .Values.nameOverride }}: {{ .Values.nameOverride }}` |

View File

@ -0,0 +1,19 @@
# Custom values for schema creation.
# This is a YAML-formatted file.
# Declare variables to be passed into your schema.
resources:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 200m
memory: 128Mi
provider:
webhook:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 300m
memory: 200Mi

View File

@ -204,6 +204,10 @@ spec:
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- with .nodeAffinity }}
nodeAffinity:
{{- toYaml . | nindent 10 }}
{{- end }}
{{- with .podAffinity }}
podAffinity:
{{- with .preferredDuringSchedulingIgnoredDuringExecution }}

View File

@ -1,94 +1,691 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": true,
"properties": {
"global": {
"affinity": {
"properties": {},
"type": "object"
},
"provider": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
"automountServiceAccountToken": {
"type": "null"
},
"commonLabels": {
"properties": {},
"type": "object"
},
"deploymentAnnotations": {
"properties": {},
"type": "object"
},
"deploymentStrategy": {
"additionalProperties": true,
"properties": {
"type": {
"enum": [
"Recreate",
"RollingUpdate"
],
"type": [
"string"
]
}
]
},
"type": "object"
},
"dnsConfig": {
"type": "null"
},
"dnsPolicy": {
"type": "null"
},
"domainFilters": {
"type": "array"
},
"env": {
"type": "array"
},
"excludeDomains": {
"type": "array"
},
"extraArgs": {
"type": "array",
"items": {
"type": "string"
}
},
"type": [
"array",
"null"
],
"uniqueItems": true
},
"extraContainers": {
"properties": {},
"type": "object"
},
"extraVolumeMounts": {
"type": "array"
},
"extraVolumes": {
"type": "array"
},
"fullnameOverride": {
"type": "null"
},
"global": {
"properties": {
"imagePullSecrets": {
"items": {
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"image": {
"additionalProperties": false,
"properties": {
"pullPolicy": {
"enum": [
"IfNotPresent",
"Always"
],
"type": "string"
},
"repository": {
"type": "string"
},
"tag": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"imagePullSecrets": {
"items": {
"type": "object"
},
"type": "array"
},
"initContainers": {
"type": "array"
},
"interval": {
"type": "string"
},
"labelFilter": {
"pattern": "^[a-zA-Z0-9._-]+=[a-zA-Z0-9._-]+$",
"type": [
"string",
"null"
]
},
"livenessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
},
"type": "object"
},
"logFormat": {
"default": "text",
"enum": [
"text",
"json"
],
"type": [
"string"
]
},
"logLevel": {
"default": "info",
"enum": [
"panic",
"debug",
"info",
"warning",
"error",
"fatal"
],
"type": [
"string"
]
},
"managedRecordTypes": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
],
"uniqueItems": true
},
"nameOverride": {
"type": "null"
},
"namespaced": {
"type": "boolean"
},
"nodeSelector": {
"properties": {},
"type": "object"
},
"podAnnotations": {
"properties": {},
"type": "object"
},
"podLabels": {
"properties": {},
"type": "object"
},
"podSecurityContext": {
"properties": {
"fsGroup": {
"type": "integer"
},
"runAsNonRoot": {
"type": "boolean"
},
"seccompProfile": {
"properties": {
"type": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
},
"policy": {
"default": "upsert-only",
"enum": [
"sync",
"upsert-only"
],
"type": [
"string"
]
},
"priorityClassName": {
"type": "null"
},
"provider": {
"properties": {
"name": {
"type": "string"
},
"webhook": {
"properties": {
"args": {
"type": "array"
},
"env": {
"type": "array"
},
"extraVolumeMounts": {
"type": "array"
},
"image": {
"properties": {
"pullPolicy": {
"type": "string"
},
"repository": {
"type": "null"
},
"tag": {
"type": "null"
}
},
"type": "object"
},
"limits": {
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
},
"type": "object"
},
"livenessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
},
"type": "object"
},
"readinessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
},
"type": "object"
},
"requests": {
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
},
"type": "object"
},
"resources": {
"properties": {},
"type": "object"
},
"securityContext": {
"properties": {},
"type": "object"
},
"service": {
"properties": {
"port": {
"type": "integer"
}
},
"type": "object"
},
"serviceMonitor": {
"properties": {
"bearerTokenFile": {
"type": "null"
},
"interval": {
"type": "null"
},
"metricRelabelings": {
"type": "array"
},
"relabelings": {
"type": "array"
},
"scheme": {
"type": "null"
},
"scrapeTimeout": {
"type": "null"
},
"tlsConfig": {
"properties": {},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
},
"rbac": {
"additionalProperties": true,
"properties": {
"additionalPermissions": {
"type": "array"
},
"create": {
"type": "boolean"
}
},
"type": "object"
},
"readinessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
},
"httpGet": {
"properties": {
"path": {
"type": "string"
},
"port": {
"type": "string"
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
},
"periodSeconds": {
"type": "integer"
},
"successThreshold": {
"type": "integer"
},
"timeoutSeconds": {
"type": "integer"
}
},
"type": "object"
},
"registry": {
"default": "txt",
"enum": [
"txt",
"aws-sd",
"dynamodb",
"noop"
],
"type": "string"
},
"resources": {
"properties": {
"limits": {
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
},
"type": "object"
},
"requests": {
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
},
"revisionHistoryLimit": {
"minimum": 0,
"type": [
"integer",
"null"
]
},
"secretConfiguration": {
"$comment": "This value is DEPRECATED as secrets should be configured external to the chart and exposed to the container via extraVolumes & extraVolumeMounts.",
"type": "object",
"properties": {
"data": {
"properties": {},
"type": "object"
},
"enabled": {
"type": "boolean"
},
"mountPath": {
"type": [
"string",
"null"
]
"type": "null"
},
"subPath": {
"type": [
"string",
"null"
]
},
"data": {
"type": "object",
"patternProperties": {
".+": {
"type": "string"
}
}
"type": "null"
}
}
},
"type": "object"
},
"securityContext": {
"properties": {
"allowPrivilegeEscalation": {
"type": "boolean"
},
"capabilities": {
"properties": {
"drop": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"privileged": {
"type": "boolean"
},
"readOnlyRootFilesystem": {
"type": "boolean"
},
"runAsGroup": {
"type": "integer"
},
"runAsNonRoot": {
"type": "boolean"
},
"runAsUser": {
"type": "integer"
}
},
"type": "object"
},
"service": {
"type": "object",
"properties": {
"annotations": {
"properties": {},
"type": "object"
},
"ipFamilies": {
"type": "array",
"items": {
"type": "string",
"enum": [
"IPv6",
"IPv4"
]
}
"IPv4",
"IPv6"
],
"type": "string"
},
"maxItems": 2,
"minItems": 0,
"type": [
"array",
"null"
],
"uniqueItems": true
},
"ipFamilyPolicy": {
"enum": [
"SingleStack",
"PreferDualStack",
"RequireDualStack",
null
],
"type": [
"string",
"null"
],
"items": {
"type": "string",
"enum": [
"SingleStack",
"PreferDualStack",
"RequireDualStack"
]
}
]
},
"port": {
"default": 7979,
"minimum": 0,
"type": "integer"
}
}
},
"type": "object"
},
"serviceAccount": {
"properties": {
"annotations": {
"properties": {},
"type": "object"
},
"automountServiceAccountToken": {
"type": "null"
},
"create": {
"type": "boolean"
},
"labels": {
"properties": {},
"type": "object"
},
"name": {
"type": "null"
}
},
"type": "object"
},
"serviceMonitor": {
"properties": {
"additionalLabels": {
"properties": {},
"type": "object"
},
"annotations": {
"properties": {},
"type": "object"
},
"bearerTokenFile": {
"type": "null"
},
"enabled": {
"type": "boolean"
},
"interval": {
"type": "null"
},
"metricRelabelings": {
"type": "array"
},
"namespace": {
"type": "null"
},
"relabelings": {
"type": "array"
},
"scheme": {
"type": "null"
},
"scrapeTimeout": {
"type": "null"
},
"targetLabels": {
"type": "array"
},
"tlsConfig": {
"properties": {},
"type": "object"
}
},
"type": "object"
},
"shareProcessNamespace": {
"type": "boolean"
},
"sources": {
"items": {
"type": "string"
},
"type": "array"
},
"terminationGracePeriodSeconds": {
"type": "null"
},
"tolerations": {
"type": "array"
},
"topologySpreadConstraints": {
"type": "array"
},
"triggerLoopOnEvent": {
"type": "boolean"
},
"txtOwnerId": {
"type": [
"string",
"null"
]
},
"txtPrefix": {
"type": [
"string",
"null"
]
},
"txtSuffix": {
"type": [
"string",
"null"
]
}
}
},
"type": "object"
}

View File

@ -4,18 +4,18 @@
global:
# -- Global image pull secrets.
imagePullSecrets: []
imagePullSecrets: [] # @schema item: object
image:
image: # @schema additionalProperties: false
# -- Image repository for the `external-dns` container.
repository: registry.k8s.io/external-dns/external-dns
# -- (string) Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set.
tag:
# -- Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set.
tag: # @schema type:[string, null]
# -- Image pull policy for the `external-dns` container.
pullPolicy: IfNotPresent
pullPolicy: IfNotPresent # @schema enum:[IfNotPresent, Always];
# -- Image pull secrets.
imagePullSecrets: []
imagePullSecrets: [] # @schema item: object
# -- (string) Override the name of the chart.
nameOverride:
@ -42,13 +42,15 @@ service:
# -- Service annotations.
annotations: {}
# -- Service HTTP port.
port: 7979
# -- Service IP families.
ipFamilies: []
# -- (string) Service IP family policy.
ipFamilyPolicy:
port: 7979 # @schema minimum:0; default:7979
# -- Service IP families (e.g. IPv4 and/or IPv6).
ipFamilies: [] # @schema type: [array, null]; item: string; itemEnum: ["IPv4", "IPv6"]; minItems:0; maxItems:2; uniqueItems: true;
# - IPv4
# - IPv6
# -- Service IP family policy.
ipFamilyPolicy: # @schema type: [string, null]; enum:[SingleStack, PreferDualStack, RequireDualStack, null];
rbac:
rbac: # @schema additionalProperties: true
# -- If `true`, create a `ClusterRole` & `ClusterRoleBinding` with access to the Kubernetes API.
create: true
# -- Additional rules to add to the `ClusterRole`.
@ -61,11 +63,11 @@ deploymentAnnotations: {}
extraContainers: {}
# -- [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).
deploymentStrategy:
type: Recreate
deploymentStrategy: # @schema additionalProperties: true
type: Recreate # @schema enum:[Recreate, RollingUpdate]; type:string; default: Recreate
# -- (int) Specify the number of old `ReplicaSets` to retain to allow rollback of the `Deployment``.
revisionHistoryLimit:
revisionHistoryLimit: # @schema type:[integer, null];minimum:0
# -- Labels to add to the `Pod`.
podLabels: {}
@ -189,10 +191,10 @@ serviceMonitor:
targetLabels: []
# -- Log level.
logLevel: info
logLevel: info # @schema enum:[panic, debug, info, warning, error, fatal]; type:string; default: "info"
# -- Log format.
logFormat: text
logFormat: text # @schema enum:["text", "json"]; type:string; default: "text"
# -- Interval for DNS updates.
interval: 1m
@ -209,19 +211,19 @@ sources:
- ingress
# -- How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`.
policy: upsert-only
policy: upsert-only # @schema enum:[sync, upsert-only]; type:string; default: "upsert-only"
# -- Specify the registry for storing ownership and labels.
# Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`.
registry: txt
registry: txt # @schema enum:[txt, aws-sd, dynamodb, noop]; default: "txt"
# -- (string) Specify an identifier for this instance of _ExternalDNS_ wWhen using a registry other than `noop`.
txtOwnerId:
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`.
txtPrefix:
txtPrefix: # @schema type:[string, null]; default: null
# -- (string) Specify a suffix for the domain names of TXT records created for the `txt` registry.
# Mutually exclusive with `txtPrefix`.
txtSuffix:
txtSuffix: # @schema type:[string, null]; default: null
# -- Limit possible target zones by domain suffixes.
domainFilters: []
@ -229,11 +231,11 @@ domainFilters: []
# -- Intentionally exclude domains from being managed.
excludeDomains: []
# -- (string) Filter resources queried for endpoints by label selector
labelFilter:
# -- Filter resources queried for endpoints by label selector
labelFilter: # @schema pattern:^[a-zA-Z0-9._-]+=[a-zA-Z0-9._-]+$; type: [string,null];
# -- Record types to manage (default: A, AAAA, CNAME)
managedRecordTypes: []
managedRecordTypes: [] # @schema type: [array, null]; item: string; uniqueItems: true;
provider:
# -- _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).
@ -294,7 +296,7 @@ provider:
relabelings: []
# -- Extra arguments to provide to _ExternalDNS_.
extraArgs: []
extraArgs: [] # @schema type: [array, null]; item: string; uniqueItems: true;
secretConfiguration:
# -- If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**).

View File

@ -210,6 +210,42 @@ Modify chart or values and validate the diff
kubectl diff -f _scratch/external-dns --recursive=true --show-managed-fields=false
```
### Helm Values
This helm chart comes with a JSON schema generated from values with [helm schema](https://github.com/losisin/helm-values-schema-json.git) plugin.
1. Install required plugin(s)
```sh
scripts/helm-tools.sh --install
```
2. Ensure that the schema is always up-to-date
```sh
scripts/helm-tools.sh --diff
```
3. When not up-to-date, update JSON schema
```sh
scripts/helm-tools.sh --schema
```
4. Runs a series of tests to verify that the chart is well-formed, linted and JSON schema is valid
```sh
scripts/helm-tools.sh --lint
```
5. Auto-generate documentation for helm charts into markdown files.
```sh
scripts/helm-tools.sh --docs
```
6. Add an entry to the chart [CHANGELOG.md](../../charts/external-dns/CHANGELOG.md) under `## UNRELEASED` section and `open` pull request
## Deploy with kubernetes manifests
> Note; kubernetes manifest are not up to date. Consider to create an `examples` folder

View File

@ -0,0 +1,145 @@
```yaml
---
title: leader election proposal
version: 0.15.1
authors: @ivankatliarchuk
creation-date: 2025-jan-30
status: not-planned
---
```
# Leader Election
In Kubernetes, **leader election** is a mechanism used by applications, controllers, or distributed systems to designate one instance or node as the "leader" that is responsible for managing specific tasks, while others operate as followers or standbys.
This ensures coordinated and fault-tolerant operations in highly available systems.
- [Kubernetes Coordinated Leader Election](https://kubernetes.io/docs/concepts/cluster-administration/coordinated-leader-election/)
- [Kubernetes Concepts: Leases](https://kubernetes.io/docs/concepts/architecture/leases/)
## **Leader Election in Kubernetes**
The leader election mechanism implemented in Go code relies on Kubernetes coordination features, specifically Lease object in the `coordination.k8s.io` API Group. Lease locks provide a way to acquire a lease on a shared resource, which can be used to determine the leader among a group of nodes.
***Leader Election Sequence Diagram***
```mermaid
sequenceDiagram
participant R1 as Replica 1 (Leader)
participant LR as Lock Resource
participant R2 as Replica 2 (Standby)
participant R3 as Replica 3 (Standby)
R1->>LR: Update Lock Resource
Note over LR: currentLeader: R1<br>timeStamp: 12:21<br>leaseDuration: 10s
loop Every polling period
R2->>LR: Poll leader status
LR-->>R2: Return lock info
R3->>LR: Poll leader status
LR-->>R3: Return lock info
end
Note over R2,R3: Replicas remain on standby<br>as long as leader is active
```
***Leader Election Flow***
```mermaid
graph TD
subgraph Active Replica
A[Replica 1]
end
subgraph Kubernetes Resource Lock
A["fa:fa-server Replica 1"] --> |Hold The Lock| C@{ label: "Lock" }
end
subgraph Standby Replicas
D["fa:fa-server Replica 2"] -->|Poll| C
E["fa:fa-server Replica 3"] -->|Poll| C["fa:fa-lock Lock"]
end
style C color:#8C52FF,fill:#A6A6A6
style A color:#8C52FF,fill:#00BF63
style D color:#000000,fill:#FFDE59
style E color:#000000,fill:#FFDE59
```
***How Leader Is Elected***
```mermaid
flowchart TD
A[Start Leader Election] -->|Replica 1 Becomes Leader| B(Update Lock Resource)
B --> C{Is Leader Active?}
C -->|Yes| D[Replicas 2 & 3 Poll Leader Status]
C -->|No| E[Trigger New Election]
E -->|New Leader Found| F[Replica X Becomes Leader]
E -->|No Leader| G[Retry Election]
F --> B
G --> C
D --> C
```
### Enable Leader Election
Minimum supported Kubernetes version is `v1.26`.
> Currently, this feature is "opt-in". The `--enable-leader-election` flag must be explicitly provided to activate it in the service.
| **Flag** | **Description** |
|:---------------------------|:------------------------------------------------------|
| `--enable-leader-election` | This flag is required to enable leader election logic |
```yml
args:
--registry=txt \
--source=fake \
--enable-leader-election
```
## **How Leader Election Works in Kubernetes**
1. **Lease API**:
- Kubernetes provides a built-in `Lease` object in the `coordination.k8s.io/v1` API group, specifically designed for leader election.
- The leader writes a lease object with metadata (such as its identity and timestamp) to signal that it is the current leader.
2. **Election Process**:
- All participating pods (or nodes) periodically check for the lease.
- The lease contains details of the current leader's identity (e.g., a pod name).
- If the lease expires or is not renewed, other contenders can try to acquire leadership by writing their identity into the lease object.
3. **Heartbeat (Lease Renewal)**:
- The current leader must periodically update the lease to retain leadership.
- If the leader fails to renew the lease within the configured timeout, leadership is relinquished, and another instance can take over.
---
### **Key Concepts**
- **Lease Duration**: Defines how long the leader is considered valid after the last lease renewal. Short lease durations result in faster failovers but higher contention and potential performance impact.
- **Leader Identity**: Usually the name or ID of the pod that holds the leadership role.
- **Backoff and Contention**: Followers typically wait and retry with a backoff period to avoid overwhelming the system when a leader is lost.
---
### **Why Leader Election is Important**
Leader election ensures that:
- **High Availability**: Fail-over to a new leader ensures availability even if the current leader goes down.
- **Data Consistency**: Only one leader acts on critical tasks, preventing duplicate work or conflicting updates.
- **Workload Distribution**: Secondary replicas can be on standby, reducing resource contention.
---
### **Use Cases**
Leader election functionality is critical for building reliable, fault-tolerant, and scalable applications on Kubernetes.
- **Cluster Upgrades**: Leader election ensures smooth cluster upgrades by designating one instance as responsible for orchestrating upgrades or managing specific components during the process.
By preventing multiple instances from making changes concurrently, it avoids conflicts and reduces downtime, ensuring consistency across the cluster.
- **Workload Running on Spot Instances**: For workloads running on cost-effective but ephemeral spot instances, leader election is crucial for resiliency.
When a spot instance running the leader is preempted, the failover process enables a standby instance to seamlessly take over leadership, ensuring continued execution of critical tasks.
- **Requirement for Disaster Recovery**: In disaster recovery scenarios, leader election provides fault tolerance by allowing another instance to take over when the primary leader becomes unavailable.
This guarantees operational continuity even in the face of unexpected failures, supporting robust disaster recovery strategies.
- **High Availability (HA) Scenarios**: In highly available systems, leader election ensures that a single active leader manages essential processes or state, while backups remain ready to step in instantly in case of failure.
This minimizes recovery time objectives (RTO) and eliminates single points of failure.
- **Enhanced Reliability in Distributed Systems**: Incorporating leader election into your distributed system enhances its overall reliability. It avoids the pitfalls of uncoordinated task execution, providing deterministic behavior and ensuring only one instance manages critical tasks at any given time.
- **Conflict Prevention**: Leader election serves as a guard against conflicts arising from multiple instances attempting to execute the same tasks. By ensuring that only the elected leader acts on shared resources or processes, it prevents data corruption, inconsistencies, and wasted computational effort.

View File

@ -4,8 +4,8 @@
title: New Feature or Deprecation/Removal Proposal
version: if applicable
authors: you, me
creation-date: 2025-01-01
status: draft
creation-date: 2025-jan-01
status: draft|approved|rejected|not-planned|partially-implemented|implemented
---
```

View File

@ -1101,8 +1101,10 @@ args:
```
Can't specify multiple or separate values with commas: `key1=val1,key2=val2` at the moment.
Filter only by value `--aws-zone-tags==tag-value` is not supported.
```sh
args:
--aws-zone-tags=team=k8s,vertical=platform
--aws-zone-tags=team=k8s,vertical=platform # this is not supported
--aws-zone-tags==tag-value # this is not supported
```

83
go.mod
View File

@ -5,25 +5,25 @@ go 1.23.5
require (
cloud.google.com/go/compute/metadata v0.6.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.0
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.6.1
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.6.2
github.com/IBM/go-sdk-core/v5 v5.18.5
github.com/IBM/networking-go-sdk v0.51.1
github.com/Yamashou/gqlgenc v0.30.3
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.84
github.com/aws/aws-sdk-go-v2 v1.36.0
github.com/aws/aws-sdk-go-v2/config v1.29.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.58
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.1
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.9
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.6
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.10
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13
github.com/aliyun/alibaba-cloud-sdk-go v1.63.88
github.com/aws/aws-sdk-go-v2 v1.36.1
github.com/aws/aws-sdk-go-v2/config v1.29.6
github.com/aws/aws-sdk-go-v2/credentials v1.17.59
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.4
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.40.1
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.7
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.11
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14
github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0
github.com/civo/civogo v0.3.93
@ -31,7 +31,7 @@ require (
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.136.0
github.com/digitalocean/godo v1.137.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,8 +47,8 @@ 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.83.0
github.com/ovh/go-ovh v1.6.0
github.com/oracle/oci-go-sdk/v65 v65.83.1
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
@ -57,22 +57,22 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1091
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1091
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1091
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1097
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1097
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1097
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.18
go.uber.org/ratelimit v0.3.1
golang.org/x/net v0.34.0
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.0
golang.org/x/sync v0.11.0
golang.org/x/time v0.10.0
google.golang.org/api v0.219.0
google.golang.org/api v0.221.0
gopkg.in/ns1/ns1-go.v2 v2.13.0
gopkg.in/yaml.v2 v2.4.0
istio.io/api v1.24.2
istio.io/client-go v1.24.2
istio.io/api v1.24.3
istio.io/client-go v1.24.3
k8s.io/api v0.32.1
k8s.io/apimachinery v0.32.1
k8s.io/client-go v0.32.1
@ -81,26 +81,26 @@ require (
)
require (
cloud.google.com/go/auth v0.14.0 // indirect
cloud.google.com/go/auth v0.14.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // 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.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.19 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.20 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.12 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -188,23 +188,24 @@ require (
go.etcd.io/etcd/api/v3 v3.5.18 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.18 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.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.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.4 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

178
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.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
@ -18,10 +18,10 @@ github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbx
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.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
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/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.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
@ -43,16 +43,16 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.0 h1:IZEN1/wXkfHfHKE89fWpOZ797NbR7CCrYjwBnXiRZxI=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.0/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.6.1 h1:XAz4wwcUlPnqLtDkeRKRRzcNXY7m/92pAaYpsHL32Ro=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.6.1/go.mod h1:1oYb5+X7zh9MDtKr9f/KjMLSz+3tZpXBqfwRdu9wiws=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.6.2 h1:MADnrzQB08k54k2sz6IyXjcaXOKxvMoayU05NG0CEuE=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.6.2/go.mod h1:1oYb5+X7zh9MDtKr9f/KjMLSz+3tZpXBqfwRdu9wiws=
github.com/IBM/go-sdk-core/v5 v5.18.5 h1:g0JRl3sYXJczB/yuDlrN6x22LJ6jIxhp0Sa4ARNW60c=
github.com/IBM/go-sdk-core/v5 v5.18.5/go.mod h1:KonTFRR+8ZSgw5cxBSYo6E4WZoY1+7n1kfHM82VcjFU=
github.com/IBM/networking-go-sdk v0.51.1 h1:xRlDFxSMejMmJ7JPFZ9cflL86uJpbqUzuL/1D/pP7/g=
@ -98,8 +98,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.84 h1:8IpC2i1mtsuUt13cbZtVCtQRSjzuMvLiDrbOJcaS+Z4=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.84/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.88 h1:87jNTxliGqU2yB3H09xCd4U3cZCmR4AkOMqWgaluo5Q=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.88/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=
@ -121,42 +121,42 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk=
github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2/config v1.29.5 h1:4lS2IB+wwkj5J43Tq/AwvnscBerBJtQQ6YS7puzCI1k=
github.com/aws/aws-sdk-go-v2/config v1.29.5/go.mod h1:SNzldMlDVbN6nWxM7XsUiNXPSa1LWlqiXtvh/1PrJGg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58 h1:/d7FUpAPU8Lf2KUdjniQvfNdlMID0Sd9pS23FJ3SS9Y=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58/go.mod h1:aVYW33Ow10CyMQGFgC0ptMRIqJWvJ4nxZb0sUiuQT/A=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.1 h1:ZUkvHnTYJj4URe24GFGjm2u5FpjKnUUV1ZiI7r4/y3g=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.1/go.mod h1:dSkBQQO1LcOD5wSHuM6FqrkgfirJs+tAKm/laeSa0Oc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k=
github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg=
github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ=
github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4=
github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.4 h1:phn1rkXqpC2IMSrYF9lC99BnvctRo4ArDG5S8XcoJMA=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.4/go.mod h1:8Nk8uFZ5rACaV8aiP31yQZPh9kasjSFMDj/GOrFT91E=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.9 h1:gC1SKYPagK6aiL6BrQOl0U5D3vSLQUw6mMaFe9DzoC8=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.9/go.mod h1:+pfCvXbSNLZ7lG+tydnY5IN4WUoz+WsGDrl2rg2DEew=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.19 h1:3n3jgZnhHXSHGHudTOtN3WHkgouE4Jw5rXkBNnPpPo8=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.19/go.mod h1:K7wcnwLh1oGGwASzdY6mryhtkPgst/CxmEw78LmlyOU=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.40.1 h1:JUvURAe0mNRzYd+1uTHEiojeyWtNPIQ5EXnDKfgKGUU=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.40.1/go.mod h1:FcMiR2AALpkrpik6JzbYu+iEfktzrs3XOq5Shk9nvik=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.20 h1:uUTR6EInXq1uf/Bz/0V9bc4jT3sKQ3UuFOjxeUVjeCM=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.20/go.mod h1:jpQRvf4Atm1US92/h+6U3NLeoygPdFid9OYw8awLEa8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.12 h1:V1h3Cxmn0tN5EhL31uvqSLKsMlPlqiYxRwAEdwNeIJ8=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.12/go.mod h1:KzXJPn2wqsZJlNSx70gmDkRDVTmyF/RRXxTP2yMxUwc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.6 h1:O7L9iEodiF07vJoXShMrw2XyeAqZhLUIXqWEitCq6EE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.6/go.mod h1:E93uWfli9RToQzVA7+bYnynKOFcYOhNWqhY1hWSMZRc=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.10 h1:G3u05YD9rurafUNF4CTVQZ+Fhb5qIPl4CrXJTU+gN0U=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.10/go.mod h1:8W5a2c+QnClUwxJp+jHnoyWxT2GywMsP7ia3fcQ0oHE=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13 h1:3LXNnmtH3TURctC23hnC0p/39Q5gre3FI7BNOiDcVWc=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.13 h1:eWoHfLIzYeUtJEuoUmD5PwTE+fLaIPN9NZ7UXd9CW0s=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.13/go.mod h1:x5t8Ve0J7JK9VHKSPSRAdBrWAgr/5hH3UeCFMLoyUGQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.7 h1:oPqYaMfI6XYKXD5jlJ4JHipkKcA2Ska3JLLz11ukf0E=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.7/go.mod h1:DFFR1FKSHaBJZF2eMW+6PsSg97pldSoHQnRx4tH2Mek=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.11 h1:0P8AxY1gL1XAMtbh5oJ4dKa19vH+hWrOunHAKgzoI6k=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.11/go.mod h1:vqJsXcYIagfc/7a+68TvAM2GogOSY6aK0bbROwq7uoM=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc=
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=
@ -257,8 +257,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.136.0 h1:DTxugljFJSMBPfEGq4KeXpnKeAHicggNqogcrw/YdZw=
github.com/digitalocean/godo v1.136.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/digitalocean/godo v1.137.0 h1:bkPG5ram9bHErbCWCKT6j/fMs8jisckhczd3IvueJHg=
github.com/digitalocean/godo v1.137.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
@ -848,10 +848,10 @@ 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.83.0 h1:KFI0oyyCTPmgevHF+QlN02Zdf23Jx1p1X+4KPyH14H8=
github.com/oracle/oci-go-sdk/v65 v65.83.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/oracle/oci-go-sdk/v65 v65.83.1 h1:Cgojq322B7NwOPOa/arfgVz+5+vRbb2lJtiADVQfgHE=
github.com/oracle/oci-go-sdk/v65 v65.83.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
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=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
@ -936,8 +936,8 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -1018,12 +1018,12 @@ 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.1091 h1:RxogX8ZCPBmZ6PY7DjnWnwGRkAkYEEinT5WNNxbLVeo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1091/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1091 h1:36WwNgrtoGKszQovUj3+0CjNsM1gMQ3a5lvZx2bkjng=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1091/go.mod h1:cWRGvOUnMQMky4oliMX1dXT6Z4CbsSGOIxaUcrD5Zvw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1091 h1:nNPu5tK4RYMzy1igVv2KDFZBdptI/hEBJYRBsEnSiZA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1091/go.mod h1:PFelV0G8C5lfhFSz/VO4ee4sfg4sEIq/g8HzlytjmQk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1097 h1:3wV4Lq32bzwtFJutzjdZGM+QYUff/E8fp0nLLugWDtI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1097/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1097 h1:bXAszuWU0a+MmQ5YDWNZYB7lhWtPgTioCFDLzuyma+A=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1097/go.mod h1:YDURTkRoR7iySup30Wvxt/SChJOZ0dV+P7NBoekQFdg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1097 h1:qg6EGXlghoTIHOLmegnxBkbkmpds2ZUVG9ti8htUU44=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1097/go.mod h1:zsaqJnLZrmwF8m5zJDuXIW9IUJ3lokrxwgBtcCupFIM=
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=
@ -1091,18 +1091,20 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
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.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
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.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
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.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=
@ -1147,8 +1149,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1210,8 +1212,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1281,13 +1283,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1295,8 +1297,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1351,8 +1353,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.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0=
google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE=
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
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=
@ -1368,8 +1370,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
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=
@ -1394,8 +1396,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -1451,10 +1453,10 @@ 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.24.2 h1:jYjcN6Iq0RPtQj/3KMFsybxmfqmjGN/dxhL7FGJEdIM=
istio.io/api v1.24.2/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I=
istio.io/client-go v1.24.2 h1:JTTfBV6dv+AAW+AfccyrdX4T1f9CpsXd1Yzo1s/IYAI=
istio.io/client-go v1.24.2/go.mod h1:dgZ9EmJzh1EECzf6nQhwNL4R6RvlyeH/RXeNeNp/MRg=
istio.io/api v1.24.3 h1:iwWWPM0uEQ+oxRHvIWoB8MQ4bjF3dRQj+M5IDVczg0M=
istio.io/api v1.24.3/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I=
istio.io/client-go v1.24.3 h1:TB8IcM3yyMCDzKRJo0YfFOUGNQmkhwH/JE/Yr3lzVAk=
istio.io/client-go v1.24.3/go.mod h1:zSyw/c4luKQKosFIHQaWAQOA0c3bODu4SahQCAMlKA4=
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=

49
internal/testutils/log.go Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright 2025 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 testutils
import (
"bytes"
"flag"
"testing"
log "github.com/sirupsen/logrus"
"k8s.io/klog/v2"
)
// LogsToBuffer redirects log(s) output to a buffer for testing purposes
//
// Usage: LogsToBuffer(t)
// Example:
//
// buf := LogsToBuffer(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 {
t.Helper()
buf := new(bytes.Buffer)
log.SetOutput(buf)
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
}

View File

@ -25,11 +25,12 @@ nav:
- TXT: docs/registry/txt.md
- DynamoDB: docs/registry/dynamodb.md
- Advanced Topics:
- Initial Design: docs/initial-design.md
- TTL: docs/ttl.md
- NAT64: docs/nat64.md
- MultiTarget: docs/proposal/multi-target.md
- Rate Limits: docs/rate-limits.md
- Initial Design: docs/initial-design.md
- Leader Election: docs/proposal/001-leader-election.md
- MultiTarget: docs/proposal/multi-target.md
- NAT64: docs/nat64.md
- Rate Limits: docs/rate-limits.md
- TTL: docs/ttl.md
- Contributing:
- Kubernetes Contributions: CONTRIBUTING.md
- Release: docs/release.

View File

@ -0,0 +1,76 @@
/*
Copyright 2025 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 aws
import (
"context"
"testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/internal/testutils"
)
func TestAWSRecordsV1(t *testing.T) {
var zones HostedZones
unmarshalTestHelper("/fixtures/160-plus-zones.yaml", &zones, t)
stub := NewRoute53APIFixtureStub(&zones)
provider := providerFilters(stub,
WithZoneIDFilters(
"Z10242883PKPS38KA4S6C", "Z10295763LSQ170JCTR78",
"Z102957NOTEXISTS", "Z09418121E8V6WT4FASZE",
),
WithDomainFilters("w2.w1.ex.com", "ex.com"),
)
ctx := context.Background()
z, err := provider.Zones(ctx)
assert.NoError(t, err)
assert.EqualValues(t, 3, len(z))
}
func TestAWSZonesFilterWithTags(t *testing.T) {
var zones HostedZones
unmarshalTestHelper("/fixtures/160-plus-zones.yaml", &zones, t)
stub := NewRoute53APIFixtureStub(&zones)
provider := providerFilters(stub,
WithZoneTagFilters([]string{"level=5", "owner=ext-dns"}),
)
ctx := context.Background()
z, err := provider.Zones(ctx)
assert.NoError(t, err)
assert.EqualValues(t, 24, len(z))
assert.Equal(t, 169, stub.calls["listtagsforresource"])
}
func TestAWSZonesSecondRequestHitsTheCache(t *testing.T) {
var zones HostedZones
unmarshalTestHelper("/fixtures/160-plus-zones.yaml", &zones, t)
stub := NewRoute53APIFixtureStub(&zones)
provider := providerFilters(stub)
ctx := context.Background()
_, err := provider.Zones(ctx)
assert.NoError(t, err)
b := testutils.LogsToBuffer(log.DebugLevel, t)
_, _ = provider.Zones(ctx)
assert.Contains(t, b.String(), "level=debug msg=\"Using cached zones list\"")
}

View File

@ -0,0 +1,148 @@
/*
Copyright 2025 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 aws
import (
"context"
"os"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/provider"
)
type HostedZones struct {
Zones []*HostedZone `yaml:"zones"`
}
type HostedZone struct {
Name string
ID string
Tags []route53types.Tag `yaml:"tags"`
}
var _ Route53API = &Route53APIFixtureStub{}
type Route53APIFixtureStub struct {
zones map[string]*route53types.HostedZone
zoneTags map[string][]route53types.Tag
calls map[string]int
}
func providerFilters(client *Route53APIFixtureStub, options ...func(awsProvider *AWSProvider)) *AWSProvider {
p := &AWSProvider{
clients: map[string]Route53API{defaultAWSProfile: client},
evaluateTargetHealth: false,
dryRun: false,
domainFilter: endpoint.NewDomainFilter([]string{}),
zoneIDFilter: provider.NewZoneIDFilter([]string{}),
zoneTypeFilter: provider.NewZoneTypeFilter(""),
zoneTagFilter: provider.NewZoneTagFilter([]string{}),
zonesCache: &zonesListCache{duration: 1 * time.Second},
}
for _, o := range options {
o(p)
}
return p
}
func WithDomainFilters(filters ...string) func(awsProvider *AWSProvider) {
return func(awsProvider *AWSProvider) {
awsProvider.domainFilter = endpoint.NewDomainFilter(filters)
}
}
func WithZoneIDFilters(filters ...string) func(awsProvider *AWSProvider) {
return func(awsProvider *AWSProvider) {
awsProvider.zoneIDFilter = provider.NewZoneIDFilter(filters)
}
}
func WithZoneTagFilters(filters []string) func(awsProvider *AWSProvider) {
return func(awsProvider *AWSProvider) {
awsProvider.zoneTagFilter = provider.NewZoneTagFilter(filters)
}
}
func NewRoute53APIFixtureStub(zones *HostedZones) *Route53APIFixtureStub {
route53Zones := make(map[string]*route53types.HostedZone)
zoneTags := make(map[string][]route53types.Tag)
for _, zone := range zones.Zones {
route53Zones[zone.ID] = &route53types.HostedZone{
Id: &zone.ID,
Name: &zone.Name,
}
zoneTags[cleanZoneID(zone.ID)] = zone.Tags
}
return &Route53APIFixtureStub{
zones: route53Zones,
zoneTags: zoneTags,
calls: make(map[string]int),
}
}
func (r Route53APIFixtureStub) ListResourceRecordSets(ctx context.Context, input *route53.ListResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
// TODO implement me
panic("implement me")
}
func (r Route53APIFixtureStub) ChangeResourceRecordSets(ctx context.Context, input *route53.ChangeResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ChangeResourceRecordSetsOutput, error) {
// TODO implement me
panic("implement me")
}
func (r Route53APIFixtureStub) CreateHostedZone(ctx context.Context, input *route53.CreateHostedZoneInput, optFns ...func(*route53.Options)) (*route53.CreateHostedZoneOutput, error) {
// TODO implement me
panic("implement me")
}
func (r Route53APIFixtureStub) ListHostedZones(ctx context.Context, input *route53.ListHostedZonesInput, optFns ...func(options *route53.Options)) (*route53.ListHostedZonesOutput, error) {
r.calls["listhostedzones"]++
output := &route53.ListHostedZonesOutput{}
for _, zone := range r.zones {
output.HostedZones = append(output.HostedZones, *zone)
}
return output, nil
}
func (r Route53APIFixtureStub) ListTagsForResource(ctx context.Context, input *route53.ListTagsForResourceInput, optFns ...func(options *route53.Options)) (*route53.ListTagsForResourceOutput, error) {
r.calls["listtagsforresource"]++
tags := r.zoneTags[*input.ResourceId]
return &route53.ListTagsForResourceOutput{
ResourceTagSet: &route53types.ResourceTagSet{
ResourceId: input.ResourceId,
ResourceType: input.ResourceType,
Tags: tags,
},
}, nil
}
func unmarshalTestHelper(input string, obj any, t *testing.T) {
t.Helper()
path, _ := os.Getwd()
file, err := os.Open(path + input)
assert.NoError(t, err)
defer file.Close()
dec := yaml.NewDecoder(file)
err = dec.Decode(obj)
assert.NoError(t, err)
}

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ import (
// ZoneTagFilter holds a list of zone tags to filter by
type ZoneTagFilter struct {
zoneTags []string
tagsMap map[string]string
}
// NewZoneTagFilter returns a new ZoneTagFilter given a list of zone tags
@ -30,22 +30,29 @@ func NewZoneTagFilter(tags []string) ZoneTagFilter {
if len(tags) == 1 && len(tags[0]) == 0 {
tags = []string{}
}
return ZoneTagFilter{zoneTags: tags}
tagsMap := make(map[string]string)
// tags pre-processing, to make sure the pre-processing is not happening at the time of filtering
for _, tag := range tags {
parts := strings.SplitN(tag, "=", 2)
key := strings.TrimSpace(parts[0])
if key == "" {
continue
}
if len(parts) == 2 {
value := strings.TrimSpace(parts[1])
tagsMap[key] = value
} else {
tagsMap[key] = ""
}
}
return ZoneTagFilter{tagsMap: tagsMap}
}
// Match checks whether a zone's set of tags matches the provided tag values
func (f ZoneTagFilter) Match(tagsMap map[string]string) bool {
for _, tagFilter := range f.zoneTags {
filterParts := strings.SplitN(tagFilter, "=", 2)
switch len(filterParts) {
case 1:
if _, hasTag := tagsMap[filterParts[0]]; !hasTag {
return false
}
case 2:
if value, hasTag := tagsMap[filterParts[0]]; !hasTag || value != filterParts[1] {
return false
}
for key, v := range f.tagsMap {
if value, hasTag := tagsMap[key]; !hasTag || (v != "" && value != v) {
return false
}
}
return true
@ -53,5 +60,5 @@ func (f ZoneTagFilter) Match(tagsMap map[string]string) bool {
// IsEmpty returns true if there are no tags for the filter
func (f ZoneTagFilter) IsEmpty() bool {
return len(f.zoneTags) == 0
return len(f.tagsMap) == 0
}

View File

@ -57,7 +57,7 @@ var basicZoneTags = []struct {
"empty tag filter matches all", []string{""}, map[string]string{"tag0": "value0"}, true,
},
{
"tag filter without key and equal sign", []string{"tag1=value1", "=haha"}, map[string]string{"tag1": "value1"}, false,
"tag filter without key and equal sign", []string{"tag1=value1", "=haha"}, map[string]string{"tag1": "value1"}, true,
},
}
@ -74,16 +74,17 @@ func TestZoneTagFilterNotSupportedFormat(t *testing.T) {
tests := []struct {
desc string
tags []string
want []string
want map[string]string
}{
{desc: "multiple or separate values with commas", tags: []string{"key1=val1,key2=val2"}, want: []string{"key1=val1,key2=val2"}},
{desc: "exclude tag", tags: []string{"!key1"}, want: []string{"!key1"}},
{desc: "exclude tags", tags: []string{"!key1=val"}, want: []string{"!key1=val"}},
{desc: "multiple or separate values with commas", tags: []string{"key1=val1,key2=val2"}, want: map[string]string{"key1": "val1,key2=val2"}},
{desc: "exclude tag", tags: []string{"!key1"}, want: map[string]string{"!key1": ""}},
{desc: "exclude tags", tags: []string{"!key1=val"}, want: map[string]string{"!key1": "val"}},
{desc: "key is empty", tags: []string{"=val"}, want: map[string]string{}},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%s", tc.desc), func(t *testing.T) {
got := NewZoneTagFilter(tc.tags)
assert.Equal(t, tc.want, got.zoneTags)
assert.Equal(t, tc.want, got.tagsMap)
})
}
}

122
scripts/helm-tools.sh Executable file
View File

@ -0,0 +1,122 @@
#!/bin/bash
set -e
# JSON Schema https://json-schema.org/
# JSON Schema spec https://json-schema.org/draft/2020-12/json-schema-validation
# Helm Schema https://helm.sh/docs/topics/charts/#schema-files
# Execute
# scripts/helm-tools.sh
# scripts/helm-tools.sh -h
# scripts/helm-tools.sh --install
# scripts/helm-tools.sh --diff
# scripts/helm-tools.sh --schema
# scripts/helm-tools.sh --lint
# scripts/helm-tools.sh --docs
show_help() {
cat << EOF
'external-dns' helm linter helper commands
Usage: $(basename "$0") <options>
-d, --diff Schema diff validation
--docs Re-generate helm documentation
-h, --help Display help
-i, --install Install required tooling
-l, --lint Lint chart
-s, --schema Generate schema
--show-docs Show available documentation
EOF
}
install() {
if [[ -x $(which helm) ]]; then
echo "installing https://github.com/losisin/helm-values-schema-json.git plugin"
helm plugin install https://github.com/losisin/helm-values-schema-json.git | true
helm plugin list | grep "schema"
echo "installing helm-docs"
go install github.com/norwoodj/helm-docs/cmd/helm-docs@latest | true
if [[ -x $(which brew) ]]; then
echo "installing chart-testing https://github.com/helm/chart-testing"
brew install chart-testing
fi
else
echo "helm is not installed"
echo "install helm https://helm.sh/docs/intro/install/ and try again"
exit 1
fi
}
update_schema() {
cd charts/external-dns
# uses .schema.yamle
helm schema
}
diff_schema() {
cd charts/external-dns
helm schema \
-output diff-schema.schema.json
trap 'rm -rf -- "diff-schema.schema.json"' EXIT
CURRENT_SCHEMA=$(cat values.schema.json)
GENERATED_SCHEMA=$(cat diff-schema.schema.json)
if [ "$CURRENT_SCHEMA" != "$GENERATED_SCHEMA" ]; then
echo "Schema must be re-generated! Run 'scripts/helm-tools.sh --schema'" 1>&2
diff -Nau diff-schema.schema.json values.schema.json
exit 1
fi
}
lint_chart() {
cd charts/external-dns
helm lint . --debug --strict \
--values values.yaml \
--values ci/ci-values.yaml
# lint with chart testing tool
ct lint --target-branch=master --check-version-increment=false
}
helm_docs() {
cd charts/external-dns
helm-docs
}
show_docs() {
open "https://github.com/losisin/helm-values-schema-json?tab=readme-ov-file"
}
function main() {
case $1 in
--show-docs)
show_docs
;;
-d|--diff)
diff_schema
;;
--docs)
helm_docs
;;
-i|--install)
install
;;
-l|--lint)
lint_chart
;;
-s|--schema)
update_schema
;;
-h|--help)
show_help
;;
*)
echo "unknown sub-command" >&2
show_help
exit 1
;;
esac
}
main "$@"

View File

@ -18,6 +18,7 @@ package source
import (
"context"
"strings"
log "github.com/sirupsen/logrus"
@ -45,7 +46,11 @@ func (ms *dedupSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, err
}
for _, ep := range endpoints {
identifier := ep.DNSName + " / " + ep.SetIdentifier + " / " + ep.Targets.String()
if ep == nil {
continue
}
identifier := strings.Join([]string{ep.RecordType, ep.DNSName, ep.SetIdentifier, ep.Targets.String()}, "/")
if _, ok := collected[identifier]; ok {
log.Debugf("Removing duplicate endpoint %s", ep)

View File

@ -90,6 +90,38 @@ func testDedupEndpoints(t *testing.T) {
{DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
},
{
"two endpoints with same dnsname, same type, and same target return one endpoint",
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
},
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
},
},
{
"two endpoints with same dnsname, different record type, and same target return two endpoints",
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", RecordType: "AAAA", Targets: endpoint.Targets{"1.2.3.4"}},
},
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", RecordType: "AAAA", Targets: endpoint.Targets{"1.2.3.4"}},
},
},
{
"two endpoints with same dnsname, one with record type, one without, and same target return two endpoints",
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
[]*endpoint.Endpoint{
{DNSName: "foo.example.org", RecordType: "A", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
},
} {
t.Run(tc.title, func(t *testing.T) {
mockSource := new(testutils.MockSource)

View File

@ -55,6 +55,7 @@ type virtualServiceSource struct {
ignoreHostnameAnnotation bool
serviceInformer coreinformers.ServiceInformer
virtualserviceInformer networkingv1alpha3informer.VirtualServiceInformer
gatewayInformer networkingv1alpha3informer.GatewayInformer
}
// NewIstioVirtualServiceSource creates a new virtualServiceSource with the given config.
@ -79,6 +80,7 @@ func NewIstioVirtualServiceSource(
serviceInformer := informerFactory.Core().V1().Services()
istioInformerFactory := istioinformers.NewSharedInformerFactoryWithOptions(istioClient, 0, istioinformers.WithNamespace(namespace))
virtualServiceInformer := istioInformerFactory.Networking().V1alpha3().VirtualServices()
gatewayInformer := istioInformerFactory.Networking().V1alpha3().Gateways()
// Add default resource event handlers to properly initialize informer.
serviceInformer.Informer().AddEventHandler(
@ -97,6 +99,14 @@ func NewIstioVirtualServiceSource(
},
)
gatewayInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("gateway added")
},
},
)
informerFactory.Start(ctx.Done())
istioInformerFactory.Start(ctx.Done())
@ -118,6 +128,7 @@ func NewIstioVirtualServiceSource(
ignoreHostnameAnnotation: ignoreHostnameAnnotation,
serviceInformer: serviceInformer,
virtualserviceInformer: virtualServiceInformer,
gatewayInformer: gatewayInformer,
}, nil
}
@ -186,7 +197,7 @@ func (sc *virtualServiceSource) AddEventHandler(ctx context.Context, handler fun
sc.virtualserviceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
}
func (sc *virtualServiceSource) getGateway(ctx context.Context, gatewayStr string, virtualService *networkingv1alpha3.VirtualService) (*networkingv1alpha3.Gateway, error) {
func (sc *virtualServiceSource) getGateway(_ context.Context, gatewayStr string, virtualService *networkingv1alpha3.VirtualService) (*networkingv1alpha3.Gateway, error) {
if gatewayStr == "" || gatewayStr == IstioMeshGateway {
// This refers to "all sidecars in the mesh"; ignore.
return nil, nil
@ -201,7 +212,7 @@ func (sc *virtualServiceSource) getGateway(ctx context.Context, gatewayStr strin
namespace = virtualService.Namespace
}
gateway, err := sc.istioClient.NetworkingV1alpha3().Gateways(namespace).Get(ctx, name, metav1.GetOptions{})
gateway, err := sc.gatewayInformer.Lister().Gateways(namespace).Get(name)
if errors.IsNotFound(err) {
log.Warnf("VirtualService (%s/%s) references non-existent gateway: %s ", virtualService.Namespace, virtualService.Name, gatewayStr)
return nil, nil

View File

@ -29,13 +29,10 @@ import (
istionetworking "istio.io/api/networking/v1alpha3"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
fakenetworking3 "istio.io/client-go/pkg/clientset/versioned/typed/networking/v1alpha3/fake"
v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
k8sclienttesting "k8s.io/client-go/testing"
"sigs.k8s.io/external-dns/endpoint"
)
@ -700,6 +697,20 @@ func testEndpointsFromVirtualServiceConfig(t *testing.T) {
t.Run(ti.title, func(t *testing.T) {
t.Parallel()
for i := range ti.ingresses {
if ti.ingresses[i].namespace == "" {
ti.ingresses[i].namespace = "test"
}
}
if ti.gwconfig.namespace == "" {
ti.gwconfig.namespace = "test"
}
if ti.vsconfig.namespace == "" {
ti.vsconfig.namespace = "test"
}
if source, err := newTestVirtualServiceSource(ti.lbServices, ti.ingresses, []fakeGatewayConfig{ti.gwconfig}); err != nil {
require.NoError(t, err)
} else if endpoints, err := source.endpointsFromVirtualService(context.Background(), ti.vsconfig.Config()); err != nil {
@ -2182,34 +2193,6 @@ func TestVirtualServiceSourceGetGateway(t *testing.T) {
Spec: istionetworking.Gateway{},
Status: v1alpha1.IstioStatus{},
}, expectedErrStr: ""},
{name: "ErrorGettingGateway", fields: fields{
virtualServiceSource: func() *virtualServiceSource {
istioFake := istiofake.NewSimpleClientset()
istioFake.NetworkingV1alpha3().(*fakenetworking3.FakeNetworkingV1alpha3).PrependReactor("get", "gateways", func(action k8sclienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &networkingv1alpha3.Gateway{}, fmt.Errorf("error getting gateway")
})
vs, _ := NewIstioVirtualServiceSource(
context.TODO(),
fake.NewSimpleClientset(),
istioFake,
"",
"",
"{{.Name}}",
false,
false,
)
return vs.(*virtualServiceSource)
}(),
}, args: args{
ctx: context.TODO(),
gatewayStr: "foo/bar",
virtualService: &networkingv1alpha3.VirtualService{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{Name: "gateway", Namespace: "error"},
Spec: istionetworking.VirtualService{},
Status: v1alpha1.IstioStatus{},
},
}, want: nil, expectedErrStr: "error getting gateway"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {