mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
Merge remote-tracking branch 'kubernetes-sigs/master'
This commit is contained in:
commit
95abe994c4
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@ -18,13 +18,13 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
cache-dependency-path: "./docs/scripts/requirements.txt"
|
||||
|
||||
- uses: actions/setup-go@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.19
|
||||
|
||||
|
23
.github/workflows/json-yaml-validate.yml
vendored
Normal file
23
.github/workflows/json-yaml-validate.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: json-yaml-validate
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # enable write permissions for pull requests
|
||||
|
||||
jobs:
|
||||
json-yaml-validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: json-yaml-validate
|
||||
uses: GrantBirki/json-yaml-validate@v1.2.0
|
||||
with:
|
||||
comment: "true" # enable comment mode
|
||||
yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)"
|
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
37
.github/workflows/staging-image-tester.yml
vendored
Normal file
37
.github/workflows/staging-image-tester.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Build all images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
checks: write # to create a new check based on the results (shogo82148/actions-goveralls)
|
||||
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install CI
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Test
|
||||
run: make build.image/multiarch
|
@ -24,12 +24,10 @@ COPY go.sum .
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN make test build.$ARCH
|
||||
|
||||
# final image
|
||||
FROM $ARCH/alpine:3.17
|
||||
FROM alpine:3.18
|
||||
|
||||
RUN apk update && apk add "libcrypto3>=3.0.8-r0" "libssl3>=3.0.8-r0" && rm -rf /var/cache/apt/*
|
||||
RUN apk update && apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4" && rm -rf /var/cache/apt/*
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
||||
|
79
Makefile
79
Makefile
@ -19,7 +19,7 @@
|
||||
cover:
|
||||
go get github.com/wadey/gocovmerge
|
||||
$(eval PKGS := $(shell go list ./... | grep -v /vendor/))
|
||||
$(eval PKGS_DELIM := $(shell echo $(PKGS) | sed -e 's/ /,/g'))
|
||||
$(eval PKGS_DELIM := $(shell echo $(PKGS) | tr / -'))
|
||||
go list -f '{{if or (len .TestGoFiles) (len .XTestGoFiles)}}go test -test.v -test.timeout=120s -covermode=count -coverprofile={{.Name}}_{{len .Imports}}_{{len .Deps}}.coverprofile -coverpkg $(PKGS_DELIM) {{.ImportPath}}{{end}}' $(PKGS) | xargs -0 sh -c
|
||||
gocovmerge `ls *.coverprofile` > cover.out
|
||||
rm *.coverprofile
|
||||
@ -90,8 +90,11 @@ IMAGE ?= us.gcr.io/k8s-artifacts-prod/external-dns/$(BINARY)
|
||||
VERSION ?= $(shell git describe --tags --always --dirty)
|
||||
BUILD_FLAGS ?= -v
|
||||
LDFLAGS ?= -X sigs.k8s.io/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s
|
||||
ARCHS = amd64 arm64v8 arm32v7
|
||||
SHELL = /bin/bash
|
||||
ARCHS = amd64 arm64 arm/v7
|
||||
ARCH ?= amd64
|
||||
DEFAULT_ARCH = amd64
|
||||
SHELL = /bin/bash
|
||||
OUTPUT_TYPE ?= docker
|
||||
|
||||
|
||||
build: build/$(BINARY)
|
||||
@ -99,37 +102,65 @@ build: build/$(BINARY)
|
||||
build/$(BINARY): $(SOURCES)
|
||||
CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.push/multiarch:
|
||||
build.push/multiarch: $(addprefix build.push-,$(ARCHS))
|
||||
arch_specific_tags=()
|
||||
for arch in $(ARCHS); do \
|
||||
image="$(IMAGE):$(VERSION)-$${arch}" ;\
|
||||
# pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\
|
||||
docker pull $${arch}/alpine:3.17 ;\
|
||||
docker pull golang:1.19 ;\
|
||||
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
|
||||
docker push $${image} ;\
|
||||
arch_specific_tags+=( "--amend $${image}" ) ;\
|
||||
image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\
|
||||
arch_specific_tags+=( " $${image}" ) ;\
|
||||
done ;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\
|
||||
for arch in $(ARCHS); do \
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate --arch $${arch} "$(IMAGE):$(VERSION)" "$(IMAGE):$(VERSION)-$${arch}" ;\
|
||||
done;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(IMAGE):$(VERSION)" \
|
||||
echo $${arch_specific_tags[@]} ;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker buildx imagetools create --tag "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\
|
||||
|
||||
build.push: build.docker
|
||||
docker push "$(IMAGE):$(VERSION)"
|
||||
build.image/multiarch: $(addprefix build.image-,$(ARCHS))
|
||||
|
||||
build.arm64v8:
|
||||
build.image:
|
||||
$(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=docker build.docker
|
||||
|
||||
build.image-amd64:
|
||||
$(MAKE) ARCH=amd64 build.image
|
||||
|
||||
build.image-arm64:
|
||||
$(MAKE) ARCH=arm64 build.image
|
||||
|
||||
build.image-arm/v7:
|
||||
$(MAKE) ARCH=arm/v7 build.image
|
||||
|
||||
build.push:
|
||||
$(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=registry build.docker
|
||||
|
||||
build.push-amd64:
|
||||
$(MAKE) ARCH=amd64 build.push
|
||||
|
||||
build.push-arm64:
|
||||
$(MAKE) ARCH=arm64 build.push
|
||||
|
||||
build.push-arm/v7:
|
||||
$(MAKE) ARCH=arm/v7 build.push
|
||||
|
||||
build.arm64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.amd64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.arm32v7:
|
||||
build.arm/v7:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.docker:
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="amd64" .
|
||||
build.setup:
|
||||
docker buildx inspect img-builder > /dev/null || docker buildx create --name img-builder --use
|
||||
|
||||
build.docker: build.setup build.$(ARCH)
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="$(ARCH)" .
|
||||
image="$(IMAGE):$(VERSION)-$(subst /,-,$(ARCH))"; \
|
||||
docker buildx build \
|
||||
--pull \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--output=type=$(OUTPUT_TYPE) \
|
||||
--platform linux/$(ARCH) \
|
||||
--build-arg ARCH="$(ARCH)" \
|
||||
--build-arg VERSION="$(VERSION)" \
|
||||
--tag $${image} .
|
||||
|
||||
build.mini:
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)-mini" --build-arg VERSION="$(VERSION)" -f Dockerfile.mini .
|
||||
@ -140,8 +171,8 @@ clean:
|
||||
# Builds and push container images to the staging bucket.
|
||||
.PHONY: release.staging
|
||||
|
||||
release.staging:
|
||||
release.staging: test
|
||||
IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch
|
||||
|
||||
release.prod:
|
||||
release.prod: test
|
||||
$(MAKE) build.push/multiarch
|
||||
|
@ -5,7 +5,7 @@ hide:
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/img/external-dns.png" width="40%" align="center" alt="ExternalDNS">
|
||||
<img src="img/external-dns.png" width="40%" align="center" alt="ExternalDNS">
|
||||
</p>
|
||||
|
||||
# ExternalDNS
|
||||
@ -176,6 +176,7 @@ The following tutorials are provided:
|
||||
* [Nginx Ingress Controller](docs/tutorials/nginx-ingress.md)
|
||||
* [NS1](docs/tutorials/ns1.md)
|
||||
* [NS Record Creation with CRD Source](docs/tutorials/ns-record.md)
|
||||
* [MX Record Creation with CRD Source](docs/tutorials/mx-record.md)
|
||||
* [OpenStack Designate](docs/tutorials/designate.md)
|
||||
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
|
||||
* [PowerDNS](docs/tutorials/pdns.md)
|
||||
|
@ -7,15 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
---
|
||||
|
||||
<!-- ## [vX.Y.Z] - UNRELEASED
|
||||
### Highlights
|
||||
## [UNRELEASED]
|
||||
|
||||
### All Changes
|
||||
- Added
|
||||
- Updated
|
||||
- Changed
|
||||
- Fixed
|
||||
- Deprecated
|
||||
- Removed -->
|
||||
|
||||
## [v1.12.2] - UNRELEASED
|
||||
|
||||
### All Changes
|
||||
|
||||
- Added support for ServiceMonitor relabelling. ([#3366](https://github.com/kubernetes-sigs/external-dns/pull/3366)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Updated chart icon path. ([#3492](https://github.com/kubernetes-sigs/external-dns/pull/3494)) [kundan2707](https://github.com/kundan2707)
|
||||
- Added RBAC for Gateway-API resources to ClusterRole. ([#3499](https://github.com/kubernetes-sigs/external-dns/pull/3499)) [@michaelvl](https://github.com/MichaelVL)
|
||||
- Added RBAC for F5 VirtualServer to ClusterRole. ([#3503](https://github.com/kubernetes-sigs/external-dns/pull/3503)) [@mikejoh](https://github.com/mikejoh)
|
||||
- Added support for running ExternalDNS with namespaced scope. ([#3403](https://github.com/kubernetes-sigs/external-dns/pull/3403)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4). ([#3516](https://github.com/kubernetes-sigs/external-dns/pull/3516)) [@stevehipwell](https://github.com/stevehipwell)
|
||||
|
||||
## [v1.12.1] - 2023-02-06
|
||||
|
||||
|
@ -2,8 +2,8 @@ apiVersion: v2
|
||||
name: external-dns
|
||||
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
|
||||
type: application
|
||||
version: 1.12.1
|
||||
appVersion: 0.13.2
|
||||
version: 1.12.2
|
||||
appVersion: 0.13.4
|
||||
keywords:
|
||||
- kubernetes
|
||||
- externaldns
|
||||
@ -12,7 +12,7 @@ keywords:
|
||||
- service
|
||||
- ingress
|
||||
home: https://github.com/kubernetes-sigs/external-dns/
|
||||
icon: https://github.com/kubernetes-sigs/external-dns/raw/master/img/external-dns.png
|
||||
icon: https://github.com/kubernetes-sigs/external-dns/raw/master/docs/img/external-dns.png
|
||||
sources:
|
||||
- https://github.com/kubernetes-sigs/external-dns/
|
||||
maintainers:
|
||||
@ -20,9 +20,15 @@ maintainers:
|
||||
email: steve.hipwell@gmail.com
|
||||
annotations:
|
||||
artifacthub.io/changes: |
|
||||
- kind: changed
|
||||
description: "Updated ExternalDNS version to v0.13.2."
|
||||
- kind: added
|
||||
description: "Added secretConfiguration.subPath to mount specific files from secret as a sub-path."
|
||||
description: "Added support for ServiceMonitor relabelling."
|
||||
- kind: changed
|
||||
description: "Changed to use registry.k8s.io instead of k8s.gcr.io."
|
||||
description: "Updated chart icon path."
|
||||
- kind: added
|
||||
description: "Added RBAC for Gateway-API resources to ClusterRole."
|
||||
- kind: added
|
||||
description: "Added RBAC for F5 VirtualServer to ClusterRole."
|
||||
- kind: added
|
||||
description: "Added support for running ExternalDNS with namespaced scope."
|
||||
- kind: changed
|
||||
description: "Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4)."
|
||||
|
@ -69,6 +69,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
||||
| `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` |
|
||||
| `interval` | The interval for DNS updates. | `1m` |
|
||||
| `triggerLoopOnEvent` | When enabled, triggers run loop on create/update/delete events in addition of regular interval. | `false` |
|
||||
| `namespaced` | When enabled, external-dns runs on namespace scope. Additionally, Role and Rolebinding will be namespaced, too. | `false` |
|
||||
| `sources` | K8s resources type to be observed for new DNS entries. | See _values.yaml_ |
|
||||
| `policy` | How DNS records are synchronized between sources and providers, available values are: `sync`, `upsert-only`. | `upsert-only` |
|
||||
| `registry` | Registry Type, available types are: `txt`, `noop`. | `txt` |
|
||||
@ -82,3 +83,36 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
||||
| `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` |
|
||||
| `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` |
|
||||
| `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` |
|
||||
|
||||
## Namespaced scoped installation
|
||||
|
||||
external-dns supports running on a namespaced only scope, too.
|
||||
If `namespaced=true` is defined, the helm chart will setup `Roles` and `RoleBindings` instead `ClusterRoles` and `ClusterRoleBindings`.
|
||||
|
||||
### Limited supported
|
||||
Not all sources are supported in namespaced scope, since some sources depends on cluster-wide resources.
|
||||
For example: Source `node` isn't supported, since `kind: Node` has scope `Cluster`.
|
||||
Sources like `istio-virtualservice` only work, if all resources like `Gateway` and `VirtualService` are present in the same
|
||||
namespaces as `external-dns`.
|
||||
|
||||
The annotation `external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP` is not supported.
|
||||
|
||||
If `namespaced` is set to `true`, please ensure that `sources` my only contains supported sources (Default: `service,ingress`.
|
||||
|
||||
### Support matrix
|
||||
|
||||
| Source | Supported | Infos |
|
||||
|------------------------|-----------|------------------------|
|
||||
| `ingress` | ✅ | |
|
||||
| `istio-gateway` | ✅ | |
|
||||
| `istio-virtualservice` | ✅ | |
|
||||
| `contour-ingressroute` | ✅ | |
|
||||
| `crd` | ✅ | |
|
||||
| `kong-tcpingress` | ✅ | |
|
||||
| `openshift-route` | ✅ | |
|
||||
| `skipper-routegroup` | ✅ | |
|
||||
| `gloo-proxy` | ✅ | |
|
||||
| `contour-httpproxy` | ✅ | |
|
||||
| `service` | ⚠️️ | NodePort not supported |
|
||||
| `node` | ❌ | |
|
||||
| `pod` | ❌ | |
|
||||
|
@ -1,12 +1,12 @@
|
||||
{{- if .Values.rbac.create -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }}
|
||||
metadata:
|
||||
name: {{ template "external-dns.fullname" . }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
rules:
|
||||
{{- if or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
{{- if and (not .Values.namespaced) (or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources)) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
@ -60,6 +60,41 @@ rules:
|
||||
resources: ["dnsendpoints/status"]
|
||||
verbs: ["*"]
|
||||
{{- end }}
|
||||
{{- if or (has "gateway-httproute" .Values.sources) (has "gateway-grpcroute" .Values.sources) (has "gateway-tlsroute" .Values.sources) (has "gateway-tcproute" .Values.sources) (has "gateway-udproute" .Values.sources) }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["gateways"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-httproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["httproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-httproute" .Values.sources }}
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-grpcroute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["grpcroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-tlsroute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["tlsroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-tcproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["tcproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-udproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["udproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gloo-proxy" .Values.sources }}
|
||||
- apiGroups: ["gloo.solo.io","gateway.solo.io"]
|
||||
resources: ["proxies","virtualservices"]
|
||||
@ -83,6 +118,11 @@ rules:
|
||||
resources: ["routegroups/status"]
|
||||
verbs: ["patch","update"]
|
||||
{{- end }}
|
||||
{{- if has "f5-virtualserver" .Values.sources }}
|
||||
- apiGroups: ["cis.f5.com"]
|
||||
resources: ["virtualservers"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- with .Values.rbac.additionalPermissions }}
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
|
@ -1,13 +1,13 @@
|
||||
{{- if .Values.rbac.create -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
kind: {{ .Values.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }}
|
||||
metadata:
|
||||
name: {{ printf "%s-viewer" (include "external-dns.fullname" .) }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }}
|
||||
name: {{ template "external-dns.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
|
@ -89,6 +89,9 @@ spec:
|
||||
- --txt-suffix={{ .Values.txtSuffix }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.namespaced }}
|
||||
- --namespace={{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
{{- range .Values.domainFilters }}
|
||||
- --domain-filter={{ . }}
|
||||
{{- end }}
|
||||
|
@ -151,6 +151,8 @@ logFormat: text
|
||||
interval: 1m
|
||||
triggerLoopOnEvent: false
|
||||
|
||||
namespaced: false
|
||||
|
||||
sources:
|
||||
- service
|
||||
- ingress
|
||||
|
@ -2,15 +2,22 @@
|
||||
timeout: 5000s
|
||||
options:
|
||||
substitution_option: ALLOW_LOOSE
|
||||
machineType: 'N1_HIGHCPU_8'
|
||||
steps:
|
||||
- name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90"
|
||||
entrypoint: make
|
||||
- name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20230206-8160eea68e"
|
||||
entrypoint: bash
|
||||
env:
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
- VERSION=$_GIT_TAG
|
||||
- PULL_BASE_REF=$_PULL_BASE_REF
|
||||
- HOME=/root
|
||||
args:
|
||||
- release.staging
|
||||
- -c
|
||||
- |
|
||||
gcloud auth configure-docker
|
||||
/buildx-entrypoint version
|
||||
apk add musl-dev gcc
|
||||
make release.staging
|
||||
substitutions:
|
||||
# _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and
|
||||
# can be used as a substitution
|
||||
|
@ -102,6 +102,14 @@ var (
|
||||
Help: "Number of Registry A records.",
|
||||
},
|
||||
)
|
||||
registryAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "registry",
|
||||
Name: "aaaa_records",
|
||||
Help: "Number of Registry AAAA records.",
|
||||
},
|
||||
)
|
||||
sourceARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
@ -110,6 +118,14 @@ var (
|
||||
Help: "Number of Source A records.",
|
||||
},
|
||||
)
|
||||
sourceAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "source",
|
||||
Name: "aaaa_records",
|
||||
Help: "Number of Source AAAA records.",
|
||||
},
|
||||
)
|
||||
verifiedARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
@ -118,6 +134,14 @@ var (
|
||||
Help: "Number of DNS A-records that exists both in source and registry.",
|
||||
},
|
||||
)
|
||||
verifiedAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "controller",
|
||||
Name: "verified_aaaa_records",
|
||||
Help: "Number of DNS AAAA-records that exists both in source and registry.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -130,8 +154,11 @@ func init() {
|
||||
prometheus.MustRegister(deprecatedSourceErrors)
|
||||
prometheus.MustRegister(controllerNoChangesTotal)
|
||||
prometheus.MustRegister(registryARecords)
|
||||
prometheus.MustRegister(registryAAAARecords)
|
||||
prometheus.MustRegister(sourceARecords)
|
||||
prometheus.MustRegister(sourceAAAARecords)
|
||||
prometheus.MustRegister(verifiedARecords)
|
||||
prometheus.MustRegister(verifiedAAAARecords)
|
||||
}
|
||||
|
||||
// Controller is responsible for orchestrating the different components.
|
||||
@ -171,8 +198,9 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
missingRecords := c.Registry.MissingRecords()
|
||||
|
||||
registryEndpointsTotal.Set(float64(len(records)))
|
||||
regARecords := filterARecords(records)
|
||||
registryARecords.Set(float64(len(regARecords)))
|
||||
regARecords, regAAAARecords := countAddressRecords(records)
|
||||
registryARecords.Set(float64(regARecords))
|
||||
registryAAAARecords.Set(float64(regAAAARecords))
|
||||
ctx = context.WithValue(ctx, provider.RecordsContextKey, records)
|
||||
|
||||
endpoints, err := c.Source.Endpoints(ctx)
|
||||
@ -182,10 +210,12 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
sourceEndpointsTotal.Set(float64(len(endpoints)))
|
||||
srcARecords := filterARecords(endpoints)
|
||||
sourceARecords.Set(float64(len(srcARecords)))
|
||||
vRecords := fetchMatchingARecords(endpoints, records)
|
||||
verifiedARecords.Set(float64(len(vRecords)))
|
||||
srcARecords, srcAAAARecords := countAddressRecords(endpoints)
|
||||
sourceARecords.Set(float64(srcARecords))
|
||||
sourceAAAARecords.Set(float64(srcAAAARecords))
|
||||
vARecords, vAAAARecords := countMatchingAddressRecords(endpoints, records)
|
||||
verifiedARecords.Set(float64(vARecords))
|
||||
verifiedAAAARecords.Set(float64(vAAAARecords))
|
||||
endpoints = c.Registry.AdjustEndpoints(endpoints)
|
||||
|
||||
if len(missingRecords) > 0 {
|
||||
@ -238,30 +268,44 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks and returns the intersection of A records in endpoint and registry.
|
||||
func fetchMatchingARecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) []string {
|
||||
aRecords := filterARecords(endpoints)
|
||||
recordsMap := make(map[string]struct{})
|
||||
// Counts the intersections of A and AAAA records in endpoint and registry.
|
||||
func countMatchingAddressRecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) (int, int) {
|
||||
recordsMap := make(map[string]map[string]struct{})
|
||||
for _, regRecord := range registryRecords {
|
||||
recordsMap[regRecord.DNSName] = struct{}{}
|
||||
if _, found := recordsMap[regRecord.DNSName]; !found {
|
||||
recordsMap[regRecord.DNSName] = make(map[string]struct{})
|
||||
}
|
||||
recordsMap[regRecord.DNSName][regRecord.RecordType] = struct{}{}
|
||||
}
|
||||
var cm []string
|
||||
for _, sourceRecord := range aRecords {
|
||||
if _, found := recordsMap[sourceRecord]; found {
|
||||
cm = append(cm, sourceRecord)
|
||||
aCount := 0
|
||||
aaaaCount := 0
|
||||
for _, sourceRecord := range endpoints {
|
||||
if _, found := recordsMap[sourceRecord.DNSName]; found {
|
||||
if _, found := recordsMap[sourceRecord.DNSName][sourceRecord.RecordType]; found {
|
||||
switch sourceRecord.RecordType {
|
||||
case endpoint.RecordTypeA:
|
||||
aCount++
|
||||
case endpoint.RecordTypeAAAA:
|
||||
aaaaCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cm
|
||||
return aCount, aaaaCount
|
||||
}
|
||||
|
||||
func filterARecords(endpoints []*endpoint.Endpoint) []string {
|
||||
var aRecords []string
|
||||
func countAddressRecords(endpoints []*endpoint.Endpoint) (int, int) {
|
||||
aCount := 0
|
||||
aaaaCount := 0
|
||||
for _, endPoint := range endpoints {
|
||||
if endPoint.RecordType == endpoint.RecordTypeA {
|
||||
aRecords = append(aRecords, endPoint.DNSName)
|
||||
switch endPoint.RecordType {
|
||||
case endpoint.RecordTypeA:
|
||||
aCount++
|
||||
case endpoint.RecordTypeAAAA:
|
||||
aaaaCount++
|
||||
}
|
||||
}
|
||||
return aRecords
|
||||
return aCount, aaaaCount
|
||||
}
|
||||
|
||||
// ScheduleRunOnce makes sure execution happens at most once per interval.
|
||||
@ -292,7 +336,7 @@ func (c *Controller) Run(ctx context.Context) {
|
||||
for {
|
||||
if c.ShouldRunOnce(time.Now()) {
|
||||
if err := c.RunOnce(ctx); err != nil {
|
||||
log.Error(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
select {
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -83,32 +84,20 @@ func (p *errorMockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
|
||||
|
||||
// ApplyChanges validates that the passed in changes satisfy the assumptions.
|
||||
func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
if len(changes.Create) != len(p.ExpectChanges.Create) {
|
||||
return errors.New("number of created records is wrong")
|
||||
if err := verifyEndpoints(changes.Create, p.ExpectChanges.Create); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.Create {
|
||||
if changes.Create[i].DNSName != p.ExpectChanges.Create[i].DNSName || !changes.Create[i].Targets.Same(p.ExpectChanges.Create[i].Targets) {
|
||||
return errors.New("created record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.UpdateNew, p.ExpectChanges.UpdateNew); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.UpdateNew {
|
||||
if changes.UpdateNew[i].DNSName != p.ExpectChanges.UpdateNew[i].DNSName || !changes.UpdateNew[i].Targets.Same(p.ExpectChanges.UpdateNew[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.UpdateOld, p.ExpectChanges.UpdateOld); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.UpdateOld {
|
||||
if changes.UpdateOld[i].DNSName != p.ExpectChanges.UpdateOld[i].DNSName || !changes.UpdateOld[i].Targets.Same(p.ExpectChanges.UpdateOld[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
}
|
||||
|
||||
for i := range changes.Delete {
|
||||
if changes.Delete[i].DNSName != p.ExpectChanges.Delete[i].DNSName || !changes.Delete[i].Targets.Same(p.ExpectChanges.Delete[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.Delete, p.ExpectChanges.Delete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ctx.Value(provider.RecordsContextKey), p.RecordsStore) {
|
||||
@ -117,6 +106,21 @@ func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyEndpoints(actual, expected []*endpoint.Endpoint) error {
|
||||
if len(actual) != len(expected) {
|
||||
return errors.New("number of records is wrong")
|
||||
}
|
||||
sort.Slice(actual, func(i, j int) bool {
|
||||
return actual[i].DNSName < actual[j].DNSName
|
||||
})
|
||||
for i := range actual {
|
||||
if actual[i].DNSName != expected[i].DNSName || !actual[i].Targets.Same(expected[i].Targets) {
|
||||
return errors.New("record is wrong")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newMockProvider creates a new mockProvider returning the given endpoints and validating the desired changes.
|
||||
func newMockProvider(endpoints []*endpoint.Endpoint, changes *plan.Changes) provider.Provider {
|
||||
dnsProvider := &mockProvider{
|
||||
@ -132,7 +136,7 @@ func TestRunOnce(t *testing.T) {
|
||||
// Fake some desired endpoints coming from our source.
|
||||
source := new(testutils.MockSource)
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}
|
||||
source.On("Endpoints").Return([]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "create-record",
|
||||
@ -144,6 +148,16 @@ func TestRunOnce(t *testing.T) {
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.4.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "create-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "update-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
// Fake some existing records in our DNS provider and validate some desired changes.
|
||||
@ -159,18 +173,32 @@ func TestRunOnce(t *testing.T) {
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"4.3.2.1"},
|
||||
},
|
||||
{
|
||||
DNSName: "update-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
{
|
||||
DNSName: "delete-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::4"},
|
||||
},
|
||||
},
|
||||
&plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{DNSName: "create-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
|
||||
{DNSName: "create-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
{DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::2"}},
|
||||
{DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.4.4"}},
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
{DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::3"}},
|
||||
{DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.8.8"}},
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
{DNSName: "delete-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::4"}},
|
||||
{DNSName: "delete-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}},
|
||||
},
|
||||
},
|
||||
@ -193,6 +221,7 @@ func TestRunOnce(t *testing.T) {
|
||||
source.AssertExpectations(t)
|
||||
// check the verified records
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func valueFromMetric(metric prometheus.Gauge) uint64 {
|
||||
@ -253,7 +282,7 @@ func TestShouldRunOnce(t *testing.T) {
|
||||
func testControllerFiltersDomains(t *testing.T, configuredEndpoints []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, providerEndpoints []*endpoint.Endpoint, expectedChanges []*plan.Changes) {
|
||||
t.Helper()
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}
|
||||
|
||||
source := new(testutils.MockSource)
|
||||
source.On("Endpoints").Return(configuredEndpoints, nil)
|
||||
@ -526,6 +555,85 @@ func TestVerifyARecords(t *testing.T) {
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func TestVerifyAAAARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords))
|
||||
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func TestARecords(t *testing.T) {
|
||||
@ -628,3 +736,50 @@ func TestMissingRecordsApply(t *testing.T) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAAAARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceAAAARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryAAAARecords))
|
||||
}
|
||||
|
18
docs/contributing/crd-source/dnsendpoint-aws-example.yaml
Normal file
18
docs/contributing/crd-source/dnsendpoint-aws-example.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: externaldns.k8s.io/v1alpha1
|
||||
kind: DNSEndpoint
|
||||
metadata:
|
||||
name: examplednsrecord
|
||||
spec:
|
||||
endpoints:
|
||||
- dnsName: subdomain.foo.bar.com
|
||||
providerSpecific:
|
||||
- name: "aws/failover"
|
||||
value: "PRIMARY"
|
||||
- name: "aws/health-check-id"
|
||||
value: "asdf1234-as12-as12-as12-asdf12345678"
|
||||
- name: "aws/evaluate-target-health"
|
||||
value: "true"
|
||||
recordType: CNAME
|
||||
setIdentifier: some-unique-id
|
||||
targets:
|
||||
- other-subdomain.foo.bar.com
|
@ -9,3 +9,7 @@ spec:
|
||||
recordType: A
|
||||
targets:
|
||||
- 192.168.99.216
|
||||
# Provider specific configurations are set like an annotation would on other sources
|
||||
providerSpecific:
|
||||
- name: external-dns.alpha.kubernetes.io/cloudflare-proxied
|
||||
value: "true"
|
||||
|
59
docs/faq.md
59
docs/faq.md
@ -178,16 +178,18 @@ You can use the host label in the metric to figure out if the request was agains
|
||||
|
||||
Here is the full list of available metrics provided by ExternalDNS:
|
||||
|
||||
| Name | Description | Type |
|
||||
| --------------------------------------------------- | ------------------------------------------------------- | ------- |
|
||||
| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge |
|
||||
| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge |
|
||||
| external_dns_registry_errors_total | Number of Registry errors | Counter |
|
||||
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
|
||||
| external_dns_source_errors_total | Number of Source errors | Counter |
|
||||
| external_dns_controller_verified_records | Number of DNS A-records that exists both in | Gauge |
|
||||
| | source & registry | |
|
||||
| Name | Description | Type |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------ | ------- |
|
||||
| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge |
|
||||
| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge |
|
||||
| external_dns_registry_errors_total | Number of Registry errors | Counter |
|
||||
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
|
||||
| external_dns_source_errors_total | Number of Source errors | Counter |
|
||||
| external_dns_controller_verified_aaaa_records | Number of DNS AAAA-records that exists both in source and registry | Gauge |
|
||||
| external_dns_controller_verified_a_records | Number of DNS A-records that exists both in source and registry | Gauge |
|
||||
| external_dns_registry_aaaa_records | Number of AAAA records in registry | Gauge |
|
||||
| external_dns_registry_a_records | Number of A records in registry | Gauge |
|
||||
| external_dns_source_aaaa_records | Number of AAAA records in source | Gauge |
|
||||
| external_dns_source_a_records | Number of A records in source | Gauge |
|
||||
|
||||
### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects?
|
||||
@ -205,7 +207,7 @@ $ docker run \
|
||||
-e EXTERNAL_DNS_SOURCE=$'service\ningress' \
|
||||
-e EXTERNAL_DNS_PROVIDER=google \
|
||||
-e EXTERNAL_DNS_DOMAIN_FILTER=$'foo.com\nbar.com' \
|
||||
registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
time="2017-08-08T14:10:26Z" level=info msg="config: &{APIServerURL: KubeConfig: Sources:[service ingress] Namespace: ...
|
||||
```
|
||||
|
||||
@ -255,24 +257,33 @@ spec:
|
||||
### Running an internal and external dns service
|
||||
|
||||
Sometimes you need to run an internal and an external dns service.
|
||||
The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external
|
||||
one to expose DNS to the internet.
|
||||
The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external one to expose DNS to the internet.
|
||||
|
||||
To do this with ExternalDNS you can use the `--annotation-filter` to specifically tie an instance of ExternalDNS to
|
||||
an instance of an ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external`
|
||||
then you can start two ExternalDNS providers one with `--annotation-filter=kubernetes.io/ingress.class in (nginx-internal)`
|
||||
and one with `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)`.
|
||||
To do this with ExternalDNS you can use the `--ingress-class` flag to specifically tie an instance of ExternalDNS to an instance of a ingress controller.
|
||||
Let's assume you have two ingress controllers, `internal` and `external`.
|
||||
You can then start two ExternalDNS providers, one with `--ingress-class=internal` and one with `--ingress-class=external`.
|
||||
|
||||
If you need to search for multiple values of said annotation, you can provide a comma separated list, like so:
|
||||
`--annotation-filter=kubernetes.io/ingress.class in (nginx-internal, alb-ingress-internal)`.
|
||||
If you need to search for multiple ingress classes, you can specify the flag multiple times, like so:
|
||||
`--ingress-class=internal --ingress-class=external`.
|
||||
|
||||
Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects.
|
||||
If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`.
|
||||
The `--ingress-class` flag will check both the `spec.ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation.
|
||||
The `spec.ingressClassName` tasks precedence over the annotation if both are supplied.
|
||||
|
||||
**Note:** Filtering based on annotation means that the external-dns controller will receive all resources of that kind and then filter on the client-side.
|
||||
In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance
|
||||
of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified
|
||||
in `--label-filter` will be passed to the controller.
|
||||
**Backward compatibility**
|
||||
|
||||
The previous `--annotation-filter` flag can still be used to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`.
|
||||
|
||||
However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source object.
|
||||
If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`.
|
||||
|
||||
Note: the `--ingress-class` flag cannot be used at the same time as the `--annotation-filter=kubernetes.io/ingress.class in (...)` flag; if you do this an error will be raised.
|
||||
|
||||
**Performance considerations**
|
||||
|
||||
Filtering based on ingress class name or annotations means that the external-dns controller will receive all resources of that kind and then filter on the client-side.
|
||||
In larger clusters with many resources which change frequently this can cause performance issues.
|
||||
If only some resources need to be managed by an instance of external-dns then label filtering can be used instead of ingress class filtering (or legacy annotation filtering).
|
||||
This means that only those resources which match the selector specified in `--label-filter` will be passed to the controller.
|
||||
|
||||
### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both?
|
||||
|
||||
|
@ -13,3 +13,68 @@ The controller will try to create the "new format" TXT records if they are not p
|
||||
Later on, the old format will be dropped and only the new format will be kept (<record_type>-<endpoint_name>).
|
||||
|
||||
Cleanup will be done by controller itself.
|
||||
|
||||
### Encryption of TXT Records
|
||||
TXT records may contain sensitive information, such as the internal ingress name or namespace, which attackers could exploit to gather information about your infrastructure.
|
||||
By encrypting TXT records, you can protect this information from unauthorized access. It is strongly recommended to encrypt all TXT records to prevent potential security breaches.
|
||||
|
||||
To enable encryption of TXT records, you can use the following parameters:
|
||||
- `--txt-encrypt-enabled=true`
|
||||
- `--txt-encrypt-aes-key=32bytesKey` (used for AES-256-GCM encryption and should be exactly 32 bytes)
|
||||
|
||||
Note that the key used for encryption should be a secure key and properly managed to ensure the security of your TXT records.
|
||||
|
||||
### Generating TXT encryption AES key
|
||||
Python
|
||||
```python
|
||||
python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
|
||||
```
|
||||
|
||||
Bash
|
||||
```shell
|
||||
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo
|
||||
```
|
||||
|
||||
OpenSSL
|
||||
```shell
|
||||
openssl rand -base64 32 | tr -- '+/' '-_'
|
||||
```
|
||||
|
||||
PowerShell
|
||||
```powershell
|
||||
# Add System.Web assembly to session, just in case
|
||||
Add-Type -AssemblyName System.Web
|
||||
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Web.Security.Membership]::GeneratePassword(32,4))).Replace("+","-").Replace("/","_")
|
||||
```
|
||||
|
||||
Terraform
|
||||
```hcl
|
||||
resource "random_password" "txt_key" {
|
||||
length = 32
|
||||
override_special = "-_"
|
||||
}
|
||||
```
|
||||
|
||||
### Manually Encrypt/Decrypt TXT Records
|
||||
|
||||
In some cases, you may need to edit labels generated by External-DNS, and in such cases, you can use simple Golang code to do that.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func main() {
|
||||
key := []byte("testtesttesttesttesttesttesttest")
|
||||
encrypted, _ := endpoint.EncryptText(
|
||||
"heritage=external-dns,external-dns/owner=example,external-dns/resource=ingress/default/example",
|
||||
key,
|
||||
nil,
|
||||
)
|
||||
decrypted, _, _ := endpoint.DecryptText(encrypted, key)
|
||||
fmt.Println(decrypted)
|
||||
}
|
||||
```
|
||||
|
@ -48,7 +48,7 @@ spec:
|
||||
- name: external-dns
|
||||
# You will need to check what the latest version is yourself:
|
||||
# https://github.com/kubernetes-sigs/external-dns/releases
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
# (optional) limit to only example.com domains; change to match the
|
||||
@ -114,7 +114,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
# (optional) limit to only example.com domains; change to match the
|
||||
|
@ -57,7 +57,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=akamai
|
||||
@ -143,7 +143,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=akamai
|
||||
|
@ -113,7 +113,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -187,7 +187,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -233,9 +233,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx" # use the one that corresponds to your ingress controller.
|
||||
spec:
|
||||
ingressClassName: nginx # use the one that corresponds to your ingress controller.
|
||||
rules:
|
||||
- host: foo.external-dns-test.com
|
||||
http:
|
||||
|
@ -24,7 +24,7 @@ as Kubernetes does with the AWS cloud provider.
|
||||
In the examples that follow, it is assumed that you configured the ALB Ingress
|
||||
Controller with the `ingress-class=alb` argument (not to be confused with the
|
||||
same argument to ExternalDNS) so that the controller will only respect Ingress
|
||||
objects with the `kubernetes.io/ingress.class` annotation set to "alb".
|
||||
objects with the `ingressClassName` field set to "alb".
|
||||
|
||||
## Deploy an example application
|
||||
|
||||
@ -80,7 +80,6 @@ kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
@ -120,7 +119,6 @@ metadata:
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||
external-dns.alpha.kubernetes.io/hostname: echoserver.mycluster.example.org, echoserver.example.org
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
@ -159,7 +157,6 @@ metadata:
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||
alb.ingress.kubernetes.io/ip-address-type: dualstack
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
|
@ -81,7 +81,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
env:
|
||||
- name: AWS_REGION
|
||||
value: us-east-1 # put your CloudMap NameSpace region
|
||||
@ -148,7 +148,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
env:
|
||||
- name: AWS_REGION
|
||||
value: us-east-1 # put your CloudMap NameSpace region
|
||||
|
@ -413,7 +413,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -508,7 +508,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -739,9 +739,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx" # use the one that corresponds to your ingress controller.
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: server.example.com
|
||||
http:
|
||||
@ -936,7 +935,7 @@ Running several fast polling ExternalDNS instances in a given account can easily
|
||||
* `--source=ingress --source=service` - specify multiple times for multiple sources
|
||||
* `--namespace=my-app`
|
||||
* `--label-filter=app in (my-app)`
|
||||
* `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)` - note that this filter would apply to services too..
|
||||
* `--ingress-class=nginx-external`
|
||||
* Limit services watched by type (not applicable to ingress or other types)
|
||||
* `--service-type-filter=LoadBalancer` default `all`
|
||||
* Limit the hosted zones considered
|
||||
@ -962,7 +961,7 @@ A simple way to implement randomised startup is with an init container:
|
||||
spec:
|
||||
initContainers:
|
||||
- name: init-jitter
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
|
@ -130,7 +130,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -201,7 +201,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -272,7 +272,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -416,9 +416,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: server.example.com
|
||||
http:
|
||||
|
@ -356,7 +356,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -424,7 +424,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -495,7 +495,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -560,9 +560,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: server.example.com
|
||||
http:
|
||||
@ -649,3 +648,9 @@ resource group:
|
||||
```bash
|
||||
$ az group delete --name "MyDnsResourceGroup"
|
||||
```
|
||||
|
||||
## More tutorials
|
||||
|
||||
A video explanantion is available here: https://www.youtube.com/watch?v=VSn6DPKIhM8&list=PLpbcUe4chE79sB7Jg7B4z3HytqUUEwcNE
|
||||
|
||||

|
||||
|
@ -46,7 +46,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -136,7 +136,7 @@ spec:
|
||||
secretName: bluecatconfig
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
volumeMounts:
|
||||
- name: bluecatconfig
|
||||
mountPath: "/etc/external-dns/"
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -59,7 +59,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
@ -74,7 +74,7 @@ rules:
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
@ -105,7 +105,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -20,6 +20,8 @@ Snippet from [Cloudflare - Getting Started](https://api.cloudflare.com/#getting-
|
||||
|
||||
API Token will be preferred for authentication if `CF_API_TOKEN` environment variable is set.
|
||||
Otherwise `CF_API_KEY` and `CF_API_EMAIL` should be set to run ExternalDNS with Cloudflare.
|
||||
You may provide the Cloudflare API token through a file by setting the
|
||||
`CF_API_TOKEN="file:/path/to/token"`.
|
||||
|
||||
When using API Token authentication, the token should be granted Zone `Read`, DNS `Edit` privileges, and access to `All zones`.
|
||||
|
||||
@ -54,7 +56,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -123,7 +125,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -26,7 +26,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -102,7 +102,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -108,7 +108,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
@ -175,7 +175,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
@ -198,9 +198,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: nginx.example.org
|
||||
http:
|
||||
|
@ -59,7 +59,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -136,7 +136,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -107,7 +107,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -35,7 +35,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
|
||||
@ -100,7 +100,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --txt-prefix=_d
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress # or service or both
|
||||
- --provider=exoscale
|
||||
@ -109,9 +109,9 @@ kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
external-dns.alpha.kubernetes.io/target: {{ Elastic-IP-address }}
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: via-ingress.example.com
|
||||
http:
|
||||
|
@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
|
@ -39,7 +39,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -56,7 +56,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
@ -71,7 +71,7 @@ rules:
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
@ -103,7 +103,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -72,7 +72,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
# Add desired Gateway API Route sources.
|
||||
- --source=gateway-httproute
|
||||
|
@ -319,7 +319,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -22,7 +22,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
@ -90,7 +90,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
|
@ -44,7 +44,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -115,7 +115,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -31,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -114,7 +114,7 @@ spec:
|
||||
First lets deploy a Kafka Stateful set, a simple example(a lot of stuff is missing) with a headless service called `ksvc`
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: kafka
|
||||
|
@ -69,7 +69,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -142,7 +142,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -69,7 +69,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains.
|
||||
@ -150,7 +150,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains.
|
||||
|
@ -28,7 +28,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -98,7 +98,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -22,7 +22,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=kong-tcpingress
|
||||
- --provider=aws
|
||||
@ -86,7 +86,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=kong-tcpingress
|
||||
- --provider=aws
|
||||
|
@ -141,8 +141,6 @@ Create the following Ingress to expose the echoserver application to the Interne
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
@ -181,7 +179,6 @@ kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: echoserver.mycluster.example.org, echoserver.example.org
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
@ -218,7 +215,6 @@ kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/ip-address-type: dualstack
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
@ -256,7 +252,6 @@ kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
zalando.org/aws-load-balancer-type: nlb
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -105,7 +105,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
28
docs/tutorials/mx-record.md
Normal file
28
docs/tutorials/mx-record.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Creating MX record with CRD source
|
||||
|
||||
You can create and manage MX records with the help of [CRD source](/docs/contributing/crd-source.md)
|
||||
and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, and `google` providers.
|
||||
|
||||
In order to start managing MX records you need to set the `--managed-record-types MX` flag.
|
||||
|
||||
```console
|
||||
external-dns --source crd --provider {aws|azure|google} --managed-record-types A --managed-record-types CNAME --managed-record-types MX
|
||||
```
|
||||
|
||||
Targets within the CRD need to be specified according to the RFC 1034 (section 3.6.1). Below is an example of
|
||||
`example.com` DNS MX record which specifies two separate targets with distinct priorities.
|
||||
|
||||
```yaml
|
||||
apiVersion: externaldns.k8s.io/v1alpha1
|
||||
kind: DNSEndpoint
|
||||
metadata:
|
||||
name: examplemxrecord
|
||||
spec:
|
||||
endpoints:
|
||||
- dnsName: example.com
|
||||
recordTTL: 180
|
||||
recordType: MX
|
||||
targets:
|
||||
- 10 mailhost1.example.com
|
||||
- 20 mailhost2.example.com
|
||||
```
|
@ -273,7 +273,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
@ -294,8 +294,6 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
@ -570,7 +568,7 @@ spec:
|
||||
- --google-project=zalando-external-dns-test
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
name: external-dns
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
@ -595,8 +593,6 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
|
@ -3,8 +3,9 @@
|
||||
This tutorial describes how to configure ExternalDNS to use the cluster nodes as source.
|
||||
Using nodes (`--source=node`) as source is possible to synchronize a DNS zone with the nodes of a cluster.
|
||||
|
||||
The node source adds an `A` record per each node `externalIP` (if not found, node's `internalIP` is used).
|
||||
The TTL record can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation.
|
||||
The node source adds an `A` record per each node `externalIP` (if not found, any IPv4 `internalIP` is used instead).
|
||||
It also adds an `AAAA` record per each node IPv6 `internalIP`.
|
||||
The TTL of the records can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation.
|
||||
|
||||
## Manifest (for cluster without RBAC enabled)
|
||||
|
||||
@ -28,7 +29,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=node # will use nodes as source
|
||||
- --provider=aws
|
||||
@ -99,7 +100,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=node # will use nodes as source
|
||||
- --provider=aws
|
||||
|
@ -61,7 +61,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -125,7 +125,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -66,7 +66,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=openshift-route
|
||||
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
@ -133,7 +133,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=openshift-route
|
||||
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
|
@ -6,16 +6,25 @@ Make sure to use the latest version of ExternalDNS for this tutorial.
|
||||
|
||||
## Creating an OCI DNS Zone
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records. Let's use `example.com` as an reference here.
|
||||
Create a DNS zone which will contain the managed DNS records. Let's use
|
||||
`example.com` as a reference here. Make note of the OCID of the compartment
|
||||
in which you created the zone; you'll need to provide that later.
|
||||
|
||||
For more information about OCI DNS see the documentation [here][1].
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
The OCI provider supports two authentication options: key-based and instance
|
||||
principals.
|
||||
|
||||
### Key-based
|
||||
|
||||
We first need to create a config file containing the information needed to connect with the OCI API.
|
||||
|
||||
Create a new file (oci.yaml) and modify the contents to match the example below. Be sure to adjust the values to match your own credentials:
|
||||
Create a new file (oci.yaml) and modify the contents to match the example
|
||||
below. Be sure to adjust the values to match your own credentials, and the OCID
|
||||
of the compartment containing the zone:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
@ -37,7 +46,29 @@ Create a secret using the config file above:
|
||||
$ kubectl create secret generic external-dns-config --from-file=oci.yaml
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
### OCI IAM Instance Principal
|
||||
|
||||
If you're running ExternalDNS within OCI, you can use OCI IAM instance
|
||||
principals to authenticate with OCI. This obviates the need to create the
|
||||
secret with your credentials. You'll need to ensure an OCI IAM policy exists
|
||||
with a statement granting the `manage dns` permission on zones and records in
|
||||
the target compartment to the dynamic group covering your instance running
|
||||
ExternalDNS.
|
||||
E.g.:
|
||||
|
||||
```
|
||||
Allow dynamic-group <dynamic-group-name> to manage dns in compartment id <target-compartment-OCID>
|
||||
```
|
||||
|
||||
You'll also need to add the `--oci-auth-instance-principal` flag to enable
|
||||
this type of authentication. Finally, you'll need to add the
|
||||
`--oci-compartment-ocid=ocid1.compartment.oc1...` flag to provide the OCID of
|
||||
the compartment containing the zone to be managed.
|
||||
|
||||
For more information about OCI IAM instance principals, see the documentation [here][2].
|
||||
For more information about OCI IAM policy details for the DNS service, see the documentation [here][3].
|
||||
|
||||
## Manifest (for clusters with RBAC enabled)
|
||||
|
||||
Apply the following manifest to deploy ExternalDNS.
|
||||
|
||||
@ -93,7 +124,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -159,3 +190,6 @@ $ kubectl apply -f nginx.yaml
|
||||
```
|
||||
|
||||
[1]: https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm
|
||||
[2]: https://docs.cloud.oracle.com/iaas/Content/Identity/Reference/dnspolicyreference.htm
|
||||
[3]: https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm
|
||||
|
||||
|
@ -86,7 +86,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -160,7 +160,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -42,7 +42,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=pdns
|
||||
|
@ -78,7 +78,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
# If authentication is disabled and/or you didn't create
|
||||
# a secret, you can remove this block.
|
||||
envFrom:
|
||||
|
@ -35,7 +35,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -105,7 +105,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -213,10 +213,10 @@ spec:
|
||||
|
||||
Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines.
|
||||
|
||||
In ExternalDNS containers args, make sure to specify `annotation-filter` and `aws-zone-type`:
|
||||
In ExternalDNS containers args, make sure to specify `aws-zone-type` and `ingress-class`:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1beta2
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
@ -241,9 +241,9 @@ spec:
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=external-dns
|
||||
- --annotation-filter=kubernetes.io/ingress.class in (external-ingress)
|
||||
- --ingress-class=external-ingress
|
||||
- --aws-zone-type=public
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
name: external-dns-public
|
||||
```
|
||||
|
||||
@ -251,10 +251,10 @@ spec:
|
||||
|
||||
Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines.
|
||||
|
||||
In ExternalDNS containers args, make sure to specify `annotation-filter` and `aws-zone-type`:
|
||||
In ExternalDNS containers args, make sure to specify `aws-zone-type` and `ingress-class`:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1beta2
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
@ -279,28 +279,27 @@ spec:
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=dev.k8s.nexus
|
||||
- --annotation-filter=kubernetes.io/ingress.class in (internal-ingress)
|
||||
- --ingress-class=internal-ingress
|
||||
- --aws-zone-type=private
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
name: external-dns-private
|
||||
```
|
||||
|
||||
## Create application Service definitions
|
||||
|
||||
For this setup to work, you've to create two Service definitions for your application.
|
||||
For this setup to work, you need to create two Ingress definitions for your application.
|
||||
|
||||
At first, create public Service definition:
|
||||
At first, create a public Ingress definition:
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "external-ingress"
|
||||
labels:
|
||||
app: app
|
||||
name: app-public
|
||||
spec:
|
||||
ingressClassName: external-ingress
|
||||
rules:
|
||||
- host: app.domain.com
|
||||
http:
|
||||
@ -313,18 +312,17 @@ spec:
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
Then create private Service definition:
|
||||
Then create a private Ingress definition:
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "internal-ingress"
|
||||
labels:
|
||||
app: app
|
||||
name: app-private
|
||||
spec:
|
||||
ingressClassName: internal-ingress
|
||||
rules:
|
||||
- host: app.domain.com
|
||||
http:
|
||||
@ -347,12 +345,12 @@ metadata:
|
||||
certmanager.k8s.io/acme-challenge-type: "dns01"
|
||||
certmanager.k8s.io/acme-dns01-provider: "route53"
|
||||
certmanager.k8s.io/cluster-issuer: "letsencrypt-production"
|
||||
kubernetes.io/ingress.class: "external-ingress"
|
||||
kubernetes.io/tls-acme: "true"
|
||||
labels:
|
||||
app: app
|
||||
name: app-public
|
||||
spec:
|
||||
ingressClassName: "external-ingress"
|
||||
rules:
|
||||
- host: app.domain.com
|
||||
http:
|
||||
@ -375,12 +373,11 @@ And reuse the requested certificate in private Service definition:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "internal-ingress"
|
||||
labels:
|
||||
app: app
|
||||
name: app-private
|
||||
spec:
|
||||
ingressClassName: "internal-ingress"
|
||||
rules:
|
||||
- host: app.domain.com
|
||||
http:
|
||||
|
@ -53,7 +53,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -120,7 +120,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -54,7 +54,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
@ -123,7 +123,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
@ -142,9 +142,8 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: nginx.lb.rancher.cloud
|
||||
http:
|
||||
|
@ -218,7 +218,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --registry=txt
|
||||
- --txt-prefix=external-dns-
|
||||
@ -260,7 +260,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --registry=txt
|
||||
- --txt-prefix=external-dns-
|
||||
|
@ -53,7 +53,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -121,7 +121,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -20,7 +20,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- ... # your arguments here
|
||||
securityContext:
|
||||
|
@ -129,7 +129,7 @@ spec:
|
||||
- --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records
|
||||
- --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service.
|
||||
- --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
imagePullPolicy: Always
|
||||
name: external-dns
|
||||
resources: {}
|
||||
|
@ -36,7 +36,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains
|
||||
@ -107,7 +107,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains
|
||||
|
@ -44,7 +44,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress # ingress is also possible
|
||||
@ -116,7 +116,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -66,7 +66,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
@ -137,7 +137,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
|
@ -42,7 +42,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -106,7 +106,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.2
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.5
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
134
endpoint/crypto.go
Normal file
134
endpoint/crypto.go
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// EncryptText gzip input data and encrypts it using the supplied AES key
|
||||
func EncryptText(text string, aesKey []byte, nonceEncoded []byte) (string, error) {
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if nonceEncoded == nil {
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if _, err = base64.StdEncoding.Decode(nonce, nonceEncoded); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
data, err := compressData([]byte(text))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cipherData := gcm.Seal(nonce, nonce, data, nil)
|
||||
return base64.StdEncoding.EncodeToString(cipherData), nil
|
||||
}
|
||||
|
||||
// DecryptText decrypt gziped data using a supplied AES encryption key ang ungzip it
|
||||
// in case of decryption failed, will return original input and decryption error
|
||||
func DecryptText(text string, aesKey []byte) (decryptResult string, encryptNonce string, err error) {
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
nonceSize := gcm.NonceSize()
|
||||
data, err := base64.StdEncoding.DecodeString(text)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if len(data) <= nonceSize {
|
||||
return "", "", fmt.Errorf("the encoded data from text %#v is shorter than %#v bytes and can't be decoded", text, nonceSize)
|
||||
}
|
||||
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
|
||||
plaindata, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
plaindata, err = decompressData(plaindata)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to decompress data based on the base64 encoded text %#v. Got error %#v.", text, err)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(plaindata), base64.StdEncoding.EncodeToString(nonce), nil
|
||||
}
|
||||
|
||||
// decompressData gzip compressed data
|
||||
func decompressData(data []byte) (resData []byte, err error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer gz.Close()
|
||||
var b bytes.Buffer
|
||||
if _, err = b.ReadFrom(gz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// compressData by gzip, for minify data stored in registry
|
||||
func compressData(data []byte) (compressedData []byte, err error) {
|
||||
var b bytes.Buffer
|
||||
gz, err := gzip.NewWriterLevel(&b, gzip.BestCompression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer gz.Close()
|
||||
if _, err = gz.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = gz.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = gz.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
58
endpoint/crypto_test.go
Normal file
58
endpoint/crypto_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
// Verify that text encryption and decryption works
|
||||
aesKey := []byte("s%zF`.*'5`9.AhI2!B,.~hmbs^.*TL?;")
|
||||
plaintext := "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
|
||||
encryptedtext, err := EncryptText(plaintext, aesKey, nil)
|
||||
require.NoError(t, err)
|
||||
decryptedtext, _, err := DecryptText(encryptedtext, aesKey)
|
||||
require.NoError(t, err)
|
||||
if plaintext != decryptedtext {
|
||||
t.Errorf("Original plain text %#v differs from the resulting decrypted text %#v", plaintext, decryptedtext)
|
||||
}
|
||||
|
||||
// Verify that decrypt returns an error and empty data if wrong AES encryption key is used
|
||||
decryptedtext, _, err = DecryptText(encryptedtext, []byte("s'J!jD`].LC?g&Oa11AgTub,j48ts/96"))
|
||||
require.Error(t, err)
|
||||
if decryptedtext != "" {
|
||||
t.Error("Data decryption failed, empty string should be as result")
|
||||
}
|
||||
|
||||
// Verify that decrypt returns an error and empty data if unencrypted input is is supplied
|
||||
decryptedtext, _, err = DecryptText(plaintext, aesKey)
|
||||
require.Error(t, err)
|
||||
if decryptedtext != "" {
|
||||
t.Errorf("Data decryption failed, empty string should be as result")
|
||||
}
|
||||
|
||||
// Verify that a known encrypted text is decrypted to what is expected
|
||||
encryptedtext = "0Mfzf6wsN8llrfX0ucDZ6nlc2+QiQfKKedjPPLu5atb2I35L9nUZeJcCnuLVW7CVW3K0h94vSuBLdXnMrj8Vcm0M09shxaoF48IcCpD03XtQbKXqk2hPbsW6+JybvplHIQGr16/PcjUSObGmR9yjf38+qEltApkKvrPjsyw43BX4eE10rL0Bln33UJD7/w+zazRDPFlAcbGtkt0ETKHnvyB3/aCddLipvrhjCXj2ZY/ktRF6h716kJRgXU10dCIQHFYU45MIdxI+k10HK3yZqhI2V0Gp2xjrFV/LRQ7/OS9SFee4asPWUYxbCEsnOzp8qc0dCPFSo1dtADzWnUZnsAcbnjtudT4milfLJc5CxDk1v3ykqQ/ajejwHjWQ7b8U6AsTErbezfdcqrb5IzkLgHb5TosnfrdDmNc9GcKfpsrCHbVY8KgNwMVdtwavLv7d9WM6sooUlZ3t0sABGkzagXQmPRvwLnkSOlie5XrnzWo8/8/4UByLga29CaXO"
|
||||
decryptedtext, _, err = DecryptText(encryptedtext, aesKey)
|
||||
require.NoError(t, err)
|
||||
if decryptedtext != plaintext {
|
||||
t.Error("Decryption of text didn't result in expected plaintext result.")
|
||||
}
|
||||
}
|
@ -30,6 +30,8 @@ import (
|
||||
const (
|
||||
// RecordTypeA is a RecordType enum value
|
||||
RecordTypeA = "A"
|
||||
// RecordTypeAAAA is a RecordType enum value
|
||||
RecordTypeAAAA = "AAAA"
|
||||
// RecordTypeCNAME is a RecordType enum value
|
||||
RecordTypeCNAME = "CNAME"
|
||||
// RecordTypeTXT is a RecordType enum value
|
||||
@ -40,6 +42,8 @@ const (
|
||||
RecordTypeNS = "NS"
|
||||
// RecordTypePTR is a RecordType enum value
|
||||
RecordTypePTR = "PTR"
|
||||
// RecordTypeMX is a RecordType enum value
|
||||
RecordTypeMX = "MX"
|
||||
)
|
||||
|
||||
// TTL is a structure defining the TTL of a DNS record
|
||||
@ -164,7 +168,7 @@ type Endpoint struct {
|
||||
DNSName string `json:"dnsName,omitempty"`
|
||||
// The targets the DNS record points to
|
||||
Targets Targets `json:"targets,omitempty"`
|
||||
// RecordType type of record, e.g. CNAME, A, SRV, TXT etc
|
||||
// RecordType type of record, e.g. CNAME, A, AAAA, SRV, TXT etc
|
||||
RecordType string `json:"recordType,omitempty"`
|
||||
// Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple')
|
||||
SetIdentifier string `json:"setIdentifier,omitempty"`
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
@ -41,6 +43,9 @@ const (
|
||||
|
||||
// DualstackLabelKey is the name of the label that identifies dualstack endpoints
|
||||
DualstackLabelKey = "dualstack"
|
||||
|
||||
// txtEncryptionNonce label for keep same nonce for same txt records, for prevent different result of encryption for same txt record, it can cause issues for some providers
|
||||
txtEncryptionNonce = "txt-encryption-nonce"
|
||||
)
|
||||
|
||||
// Labels store metadata related to the endpoint
|
||||
@ -55,7 +60,7 @@ func NewLabels() Labels {
|
||||
// NewLabelsFromString constructs endpoints labels from a provided format string
|
||||
// if heritage set to another value is found then error is returned
|
||||
// no heritage automatically assumes is not owned by external-dns and returns invalidHeritage error
|
||||
func NewLabelsFromString(labelText string) (Labels, error) {
|
||||
func NewLabelsFromStringPlain(labelText string) (Labels, error) {
|
||||
endpointLabels := map[string]string{}
|
||||
labelText = strings.Trim(labelText, "\"") // drop quotes
|
||||
tokens := strings.Split(labelText, ",")
|
||||
@ -85,9 +90,26 @@ func NewLabelsFromString(labelText string) (Labels, error) {
|
||||
return endpointLabels, nil
|
||||
}
|
||||
|
||||
// Serialize transforms endpoints labels into a external-dns recognizable format string
|
||||
func NewLabelsFromString(labelText string, aesKey []byte) (Labels, error) {
|
||||
if len(aesKey) != 0 {
|
||||
decryptedText, encryptionNonce, err := DecryptText(strings.Trim(labelText, "\""), aesKey)
|
||||
//in case if we have decryption error, just try process original text
|
||||
//decryption errors should be ignored here, because we can already have plain-text labels in registry
|
||||
if err == nil {
|
||||
labels, err := NewLabelsFromStringPlain(decryptedText)
|
||||
if err == nil {
|
||||
labels[txtEncryptionNonce] = encryptionNonce
|
||||
}
|
||||
|
||||
return labels, err
|
||||
}
|
||||
}
|
||||
return NewLabelsFromStringPlain(labelText)
|
||||
}
|
||||
|
||||
// SerializePlain transforms endpoints labels into a external-dns recognizable format string
|
||||
// withQuotes adds additional quotes
|
||||
func (l Labels) Serialize(withQuotes bool) string {
|
||||
func (l Labels) SerializePlain(withQuotes bool) string {
|
||||
var tokens []string
|
||||
tokens = append(tokens, fmt.Sprintf("heritage=%s", heritage))
|
||||
var keys []string
|
||||
@ -104,3 +126,31 @@ func (l Labels) Serialize(withQuotes bool) string {
|
||||
}
|
||||
return strings.Join(tokens, ",")
|
||||
}
|
||||
|
||||
// Serialize same to SerializePlain, but encrypt data, if encryption enabled
|
||||
func (l Labels) Serialize(withQuotes bool, txtEncryptEnabled bool, aesKey []byte) string {
|
||||
if !txtEncryptEnabled {
|
||||
return l.SerializePlain(withQuotes)
|
||||
}
|
||||
|
||||
var encryptionNonce []byte = nil
|
||||
if extractedNonce, nonceExists := l[txtEncryptionNonce]; nonceExists {
|
||||
encryptionNonce = []byte(extractedNonce)
|
||||
delete(l, txtEncryptionNonce)
|
||||
}
|
||||
|
||||
text := l.SerializePlain(false)
|
||||
log.Debugf("Encrypt the serialized text %#v before returning it.", text)
|
||||
var err error
|
||||
text, err = EncryptText(text, aesKey, encryptionNonce)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encrypt the text %#v using the encryption key %#v. Got error %#v.", text, aesKey, err)
|
||||
}
|
||||
|
||||
if withQuotes {
|
||||
text = fmt.Sprintf("\"%s\"", text)
|
||||
}
|
||||
log.Debugf("Serialized text after encryption is %#v.", text)
|
||||
return text
|
||||
}
|
||||
|
@ -25,14 +25,18 @@ import (
|
||||
|
||||
type LabelsSuite struct {
|
||||
suite.Suite
|
||||
foo Labels
|
||||
fooAsText string
|
||||
fooAsTextWithQuotes string
|
||||
barText string
|
||||
barTextAsMap Labels
|
||||
noHeritageText string
|
||||
wrongHeritageText string
|
||||
multipleHeritageText string // considered invalid
|
||||
aesKey []byte
|
||||
foo Labels
|
||||
fooAsText string
|
||||
fooAsTextWithQuotes string
|
||||
fooAsTextEncrypted string
|
||||
fooAsTextWithQuotesEncrypted string
|
||||
barText string
|
||||
barTextEncrypted string
|
||||
barTextAsMap Labels
|
||||
noHeritageText string
|
||||
wrongHeritageText string
|
||||
multipleHeritageText string // considered invalid
|
||||
}
|
||||
|
||||
func (suite *LabelsSuite) SetupTest() {
|
||||
@ -40,48 +44,79 @@ func (suite *LabelsSuite) SetupTest() {
|
||||
"owner": "foo-owner",
|
||||
"resource": "foo-resource",
|
||||
}
|
||||
suite.aesKey = []byte(")K_Fy|?Z.64#UuHm`}[d!GC%WJM_fs{_")
|
||||
suite.fooAsText = "heritage=external-dns,external-dns/owner=foo-owner,external-dns/resource=foo-resource"
|
||||
suite.fooAsTextWithQuotes = fmt.Sprintf(`"%s"`, suite.fooAsText)
|
||||
|
||||
suite.fooAsTextEncrypted = `+lvP8q9KHJ6BS6O81i2Q6DLNdf2JSKy8j/gbZKviTZlGYj7q+yDoYMgkQ1hPn6urtGllM5bfFMcaaHto52otQtiOYrX8990J3kQqg4s47m3bH3Ejl8RSxSSuWJM3HJtPghQzYg0/LSOsdQ0=`
|
||||
suite.fooAsTextWithQuotesEncrypted = fmt.Sprintf(`"%s"`, suite.fooAsTextEncrypted)
|
||||
suite.barTextAsMap = map[string]string{
|
||||
"owner": "bar-owner",
|
||||
"resource": "bar-resource",
|
||||
"new-key": "bar-new-key",
|
||||
}
|
||||
suite.barText = "heritage=external-dns,,external-dns/owner=bar-owner,external-dns/resource=bar-resource,external-dns/new-key=bar-new-key,random=stuff,no-equal-sign,," // also has some random gibberish
|
||||
|
||||
suite.barTextEncrypted = "yi6vVATlgYN0enXBIupVK2atNUKtajofWMroWtvZjUanFZXlWvqjJPpjmMd91kv86bZj+syQEP0uR3TK6eFVV7oKFh/NxYyh238FjZ+25zlXW9TgbLoMalUNOkhKFdfXkLeeaqJjePB59t+kQBYX+ZEryK652asPs6M+xTIvtg07N7WWZ6SjJujm0RRISg=="
|
||||
suite.noHeritageText = "external-dns/owner=random-owner"
|
||||
suite.wrongHeritageText = "heritage=mate,external-dns/owner=random-owner"
|
||||
suite.multipleHeritageText = "heritage=mate,heritage=external-dns,external-dns/owner=random-owner"
|
||||
}
|
||||
|
||||
func (suite *LabelsSuite) TestSerialize() {
|
||||
suite.Equal(suite.fooAsText, suite.foo.Serialize(false), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsText, suite.foo.SerializePlain(false), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsTextWithQuotes, suite.foo.SerializePlain(true), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsText, suite.foo.Serialize(false, false, nil), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, false, nil), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsText, suite.foo.Serialize(false, false, suite.aesKey), "should serializeLabel")
|
||||
suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, false, suite.aesKey), "should serializeLabel")
|
||||
suite.NotEqual(suite.fooAsText, suite.foo.Serialize(false, true, suite.aesKey), "should serializeLabel and encrypt")
|
||||
suite.NotEqual(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, true, suite.aesKey), "should serializeLabel and encrypt")
|
||||
}
|
||||
|
||||
func (suite *LabelsSuite) TestEncryptionNonceReUsage() {
|
||||
foo, err := NewLabelsFromString(suite.fooAsTextEncrypted, suite.aesKey)
|
||||
suite.NoError(err, "should succeed for valid label text")
|
||||
serialized := foo.Serialize(false, true, suite.aesKey)
|
||||
suite.Equal(serialized, suite.fooAsTextEncrypted, "serialized result should be equal")
|
||||
}
|
||||
|
||||
func (suite *LabelsSuite) TestDeserialize() {
|
||||
foo, err := NewLabelsFromString(suite.fooAsText)
|
||||
foo, err := NewLabelsFromStringPlain(suite.fooAsText)
|
||||
suite.NoError(err, "should succeed for valid label text")
|
||||
suite.Equal(suite.foo, foo, "should reconstruct original label map")
|
||||
|
||||
foo, err = NewLabelsFromString(suite.fooAsTextWithQuotes)
|
||||
foo, err = NewLabelsFromStringPlain(suite.fooAsTextWithQuotes)
|
||||
suite.NoError(err, "should succeed for valid label text")
|
||||
suite.Equal(suite.foo, foo, "should reconstruct original label map")
|
||||
|
||||
bar, err := NewLabelsFromString(suite.barText)
|
||||
foo, err = NewLabelsFromString(suite.fooAsTextEncrypted, suite.aesKey)
|
||||
suite.NoError(err, "should succeed for valid encrypted label text")
|
||||
for key, val := range suite.foo {
|
||||
suite.Equal(val, foo[key], "should contains all keys from original label map")
|
||||
}
|
||||
|
||||
foo, err = NewLabelsFromString(suite.fooAsTextWithQuotesEncrypted, suite.aesKey)
|
||||
suite.NoError(err, "should succeed for valid encrypted label text")
|
||||
for key, val := range suite.foo {
|
||||
suite.Equal(val, foo[key], "should contains all keys from original label map")
|
||||
}
|
||||
|
||||
bar, err := NewLabelsFromStringPlain(suite.barText)
|
||||
suite.NoError(err, "should succeed for valid label text")
|
||||
suite.Equal(suite.barTextAsMap, bar, "should reconstruct original label map")
|
||||
|
||||
noHeritage, err := NewLabelsFromString(suite.noHeritageText)
|
||||
bar, err = NewLabelsFromString(suite.barText, suite.aesKey)
|
||||
suite.NoError(err, "should succeed for valid encrypted label text")
|
||||
suite.Equal(suite.barTextAsMap, bar, "should reconstruct original label map")
|
||||
|
||||
noHeritage, err := NewLabelsFromStringPlain(suite.noHeritageText)
|
||||
suite.Equal(ErrInvalidHeritage, err, "should fail if no heritage is found")
|
||||
suite.Nil(noHeritage, "should return nil")
|
||||
|
||||
wrongHeritage, err := NewLabelsFromString(suite.wrongHeritageText)
|
||||
wrongHeritage, err := NewLabelsFromStringPlain(suite.wrongHeritageText)
|
||||
suite.Equal(ErrInvalidHeritage, err, "should fail if wrong heritage is found")
|
||||
suite.Nil(wrongHeritage, "if error should return nil")
|
||||
|
||||
multipleHeritage, err := NewLabelsFromString(suite.multipleHeritageText)
|
||||
multipleHeritage, err := NewLabelsFromStringPlain(suite.multipleHeritageText)
|
||||
suite.Equal(ErrInvalidHeritage, err, "should fail if multiple heritage is found")
|
||||
suite.Nil(multipleHeritage, "if error should return nil")
|
||||
}
|
||||
|
12
go.mod
12
go.mod
@ -42,7 +42,7 @@ require (
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/openshift/api v0.0.0-20210315202829-4b79815405ec
|
||||
github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible
|
||||
github.com/oracle/oci-go-sdk/v65 v65.35.0
|
||||
github.com/ovh/go-ovh v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pluralsh/gqlclient v1.1.6
|
||||
@ -58,8 +58,8 @@ require (
|
||||
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556
|
||||
github.com/vultr/govultr/v2 v2.17.2
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/v3 v3.5.5
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/v3 v3.5.8
|
||||
go.uber.org/ratelimit v0.2.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/oauth2 v0.5.0
|
||||
@ -113,6 +113,7 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
@ -164,11 +165,12 @@ require (
|
||||
github.com/schollz/progressbar/v3 v3.8.6 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/smartystreets/gunit v1.3.4 // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/terra-farm/udnssdk v1.3.5 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8 // indirect
|
||||
go.mongodb.org/mongo-driver v1.5.1 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
@ -176,7 +178,7 @@ require (
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
|
45
go.sum
45
go.sum
@ -153,7 +153,6 @@ github.com/ans-group/go-durationstring v1.2.0 h1:UJIuQATkp0t1rBvZsHRwki33YHV9E+U
|
||||
github.com/ans-group/go-durationstring v1.2.0/go.mod h1:QGF9Mdpq9058QXaut8r55QWu6lcHX6i/GvF1PZVkV6o=
|
||||
github.com/ans-group/sdk-go v1.10.4 h1:wZzojt99wtVIEHs8zNQzp1Xhqme5tD5NqMM1VLmG6xQ=
|
||||
github.com/ans-group/sdk-go v1.10.4/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
@ -218,8 +217,6 @@ github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/cncf/udpa v0.0.0-20200324003616-bae28a880fdb/go.mod h1:HNVadOiXCy7Jk3R2knJ+qm++zkncJxxBMpjdGgJ+UJc=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200324003616-bae28a880fdb/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
@ -314,9 +311,6 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200609174644-bd816e4522c1/go.mod h1:bjmEhrMDubXDd0uKxnWwRmgSsiEv2CkJliIHnj6ETm8=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@ -484,6 +478,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg=
|
||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
@ -623,7 +619,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
@ -950,8 +945,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.35.0 h1:zvDsEuGs0qf6hPZVbrDnnfPJYQP7CwAgidTr4Pch6E4=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.35.0/go.mod h1:MXMLMzHnnd9wlpgadPkdlkZ9YrwQmCOmbX5kjVEJodw=
|
||||
github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk=
|
||||
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
|
||||
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
|
||||
@ -995,7 +990,6 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD
|
||||
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
@ -1039,7 +1033,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -1089,6 +1082,8 @@ github.com/smartystreets/gunit v1.3.4 h1:iHc8Rfhb/uCOc9a3KGuD3ut22L+hLIVaqR1o5fS
|
||||
github.com/smartystreets/gunit v1.3.4/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
@ -1201,12 +1196,12 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||
go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=
|
||||
go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI=
|
||||
go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c=
|
||||
go.etcd.io/etcd/api/v3 v3.5.8 h1:Zf44zJszoU7zRV0X/nStPenegNXoFDWcB/MwrJbA+L4=
|
||||
go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8 h1:tPp9YRn/UBFAHdhOQUII9eUs7aOK35eulpMhX4YBd+M=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.8 h1:B6ngTKZSWWowHEoaucOKHQR/AtZKaoHLiUpWxOLG4l4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
@ -1221,7 +1216,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
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=
|
||||
@ -1239,7 +1233,6 @@ go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6m
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@ -1298,7 +1291,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
@ -1467,7 +1459,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1487,8 +1478,8 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -1587,7 +1578,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||
@ -1651,7 +1641,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
@ -1660,7 +1649,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio=
|
||||
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
@ -1682,11 +1670,7 @@ google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@ -1745,7 +1729,6 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -3,7 +3,7 @@ kind: Kustomization
|
||||
|
||||
images:
|
||||
- name: registry.k8s.io/external-dns/external-dns
|
||||
newTag: v0.13.2
|
||||
newTag: v0.13.5
|
||||
|
||||
resources:
|
||||
- ./external-dns-deployment.yaml
|
||||
|
20
main.go
20
main.go
@ -18,6 +18,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -113,6 +114,7 @@ func main() {
|
||||
Namespace: cfg.Namespace,
|
||||
AnnotationFilter: cfg.AnnotationFilter,
|
||||
LabelFilter: labelSelector,
|
||||
IngressClassNames: cfg.IngressClassNames,
|
||||
FQDNTemplate: cfg.FQDNTemplate,
|
||||
CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
|
||||
IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation,
|
||||
@ -139,6 +141,8 @@ func main() {
|
||||
RequestTimeout: cfg.RequestTimeout,
|
||||
DefaultTargets: cfg.DefaultTargets,
|
||||
OCPRouterName: cfg.OCPRouterName,
|
||||
UpdateEvents: cfg.UpdateEvents,
|
||||
ResolveLoadBalancerHostname: cfg.ResolveServiceLoadBalancerHostname,
|
||||
}
|
||||
|
||||
// Lookup all the selected sources by names and pass them the desired configuration.
|
||||
@ -312,7 +316,19 @@ func main() {
|
||||
)
|
||||
case "oci":
|
||||
var config *oci.OCIConfig
|
||||
config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
|
||||
// if the instance-principals flag was set, and a compartment OCID was provided, then ignore the
|
||||
// OCI config file, and provide a config that uses instance principal authentication.
|
||||
if cfg.OCIAuthInstancePrincipal {
|
||||
if len(cfg.OCICompartmentOCID) == 0 {
|
||||
err = fmt.Errorf("instance principal authentication requested, but no compartment OCID provided")
|
||||
} else {
|
||||
authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true}
|
||||
config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID}
|
||||
}
|
||||
} else {
|
||||
config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.DryRun)
|
||||
}
|
||||
@ -367,7 +383,7 @@ func main() {
|
||||
case "noop":
|
||||
r, err = registry.NewNoopRegistry(p)
|
||||
case "txt":
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes)
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey))
|
||||
case "aws-sd":
|
||||
r, err = registry.NewAWSSDRegistry(p.(*awssd.AWSSDProvider), cfg.TXTOwnerID)
|
||||
default:
|
||||
|
@ -43,162 +43,168 @@ var Version = "unknown"
|
||||
|
||||
// Config is a project-wide configuration
|
||||
type Config struct {
|
||||
APIServerURL string
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
DefaultTargets []string
|
||||
ContourLoadBalancerService string
|
||||
GlooNamespace string
|
||||
SkipperRouteGroupVersion string
|
||||
Sources []string
|
||||
Namespace string
|
||||
AnnotationFilter string
|
||||
LabelFilter string
|
||||
FQDNTemplate string
|
||||
CombineFQDNAndAnnotation bool
|
||||
IgnoreHostnameAnnotation bool
|
||||
IgnoreIngressTLSSpec bool
|
||||
IgnoreIngressRulesSpec bool
|
||||
GatewayNamespace string
|
||||
GatewayLabelFilter string
|
||||
Compatibility string
|
||||
PublishInternal bool
|
||||
PublishHostIP bool
|
||||
AlwaysPublishNotReadyAddresses bool
|
||||
ConnectorSourceServer string
|
||||
Provider string
|
||||
GoogleProject string
|
||||
GoogleBatchChangeSize int
|
||||
GoogleBatchChangeInterval time.Duration
|
||||
GoogleZoneVisibility string
|
||||
DomainFilter []string
|
||||
ExcludeDomains []string
|
||||
RegexDomainFilter *regexp.Regexp
|
||||
RegexDomainExclusion *regexp.Regexp
|
||||
ZoneNameFilter []string
|
||||
ZoneIDFilter []string
|
||||
TargetNetFilter []string
|
||||
ExcludeTargetNets []string
|
||||
AlibabaCloudConfigFile string
|
||||
AlibabaCloudZoneType string
|
||||
AWSZoneType string
|
||||
AWSZoneTagFilter []string
|
||||
AWSAssumeRole string
|
||||
AWSAssumeRoleExternalID string
|
||||
AWSBatchChangeSize int
|
||||
AWSBatchChangeInterval time.Duration
|
||||
AWSEvaluateTargetHealth bool
|
||||
AWSAPIRetries int
|
||||
AWSPreferCNAME bool
|
||||
AWSZoneCacheDuration time.Duration
|
||||
AWSSDServiceCleanup bool
|
||||
AzureConfigFile string
|
||||
AzureResourceGroup string
|
||||
AzureSubscriptionID string
|
||||
AzureUserAssignedIdentityClientID string
|
||||
BluecatDNSConfiguration string
|
||||
BluecatConfigFile string
|
||||
BluecatDNSView string
|
||||
BluecatGatewayHost string
|
||||
BluecatRootZone string
|
||||
BluecatDNSServerName string
|
||||
BluecatDNSDeployType string
|
||||
BluecatSkipTLSVerify bool
|
||||
CloudflareProxied bool
|
||||
CloudflareDNSRecordsPerPage int
|
||||
CoreDNSPrefix string
|
||||
RcodezeroTXTEncrypt bool
|
||||
AkamaiServiceConsumerDomain string
|
||||
AkamaiClientToken string
|
||||
AkamaiClientSecret string
|
||||
AkamaiAccessToken string
|
||||
AkamaiEdgercPath string
|
||||
AkamaiEdgercSection string
|
||||
InfobloxGridHost string
|
||||
InfobloxWapiPort int
|
||||
InfobloxWapiUsername string
|
||||
InfobloxWapiPassword string `secure:"yes"`
|
||||
InfobloxWapiVersion string
|
||||
InfobloxSSLVerify bool
|
||||
InfobloxView string
|
||||
InfobloxMaxResults int
|
||||
InfobloxFQDNRegEx string
|
||||
InfobloxNameRegEx string
|
||||
InfobloxCreatePTR bool
|
||||
InfobloxCacheDuration int
|
||||
DynCustomerName string
|
||||
DynUsername string
|
||||
DynPassword string `secure:"yes"`
|
||||
DynMinTTLSeconds int
|
||||
OCIConfigFile string
|
||||
InMemoryZones []string
|
||||
OVHEndpoint string
|
||||
OVHApiRateLimit int
|
||||
PDNSServer string
|
||||
PDNSAPIKey string `secure:"yes"`
|
||||
PDNSTLSEnabled bool
|
||||
TLSCA string
|
||||
TLSClientCert string
|
||||
TLSClientCertKey string
|
||||
Policy string
|
||||
Registry string
|
||||
TXTOwnerID string
|
||||
TXTPrefix string
|
||||
TXTSuffix string
|
||||
Interval time.Duration
|
||||
MinEventSyncInterval time.Duration
|
||||
Once bool
|
||||
DryRun bool
|
||||
UpdateEvents bool
|
||||
LogFormat string
|
||||
MetricsAddress string
|
||||
LogLevel string
|
||||
TXTCacheInterval time.Duration
|
||||
TXTWildcardReplacement string
|
||||
ExoscaleEndpoint string
|
||||
ExoscaleAPIKey string `secure:"yes"`
|
||||
ExoscaleAPISecret string `secure:"yes"`
|
||||
CRDSourceAPIVersion string
|
||||
CRDSourceKind string
|
||||
ServiceTypeFilter []string
|
||||
CFAPIEndpoint string
|
||||
CFUsername string
|
||||
CFPassword string
|
||||
RFC2136Host string
|
||||
RFC2136Port int
|
||||
RFC2136Zone string
|
||||
RFC2136Insecure bool
|
||||
RFC2136GSSTSIG bool
|
||||
RFC2136KerberosRealm string
|
||||
RFC2136KerberosUsername string
|
||||
RFC2136KerberosPassword string `secure:"yes"`
|
||||
RFC2136TSIGKeyName string
|
||||
RFC2136TSIGSecret string `secure:"yes"`
|
||||
RFC2136TSIGSecretAlg string
|
||||
RFC2136TAXFR bool
|
||||
RFC2136MinTTL time.Duration
|
||||
RFC2136BatchChangeSize int
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
NS1MinTTLSeconds int
|
||||
TransIPAccountName string
|
||||
TransIPPrivateKeyFile string
|
||||
DigitalOceanAPIPageSize int
|
||||
ManagedDNSRecordTypes []string
|
||||
GoDaddyAPIKey string `secure:"yes"`
|
||||
GoDaddySecretKey string `secure:"yes"`
|
||||
GoDaddyTTL int64
|
||||
GoDaddyOTE bool
|
||||
OCPRouterName string
|
||||
IBMCloudProxied bool
|
||||
IBMCloudConfigFile string
|
||||
TencentCloudConfigFile string
|
||||
TencentCloudZoneType string
|
||||
PiholeServer string
|
||||
PiholePassword string `secure:"yes"`
|
||||
PiholeTLSInsecureSkipVerify bool
|
||||
PluralCluster string
|
||||
PluralProvider string
|
||||
APIServerURL string
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
DefaultTargets []string
|
||||
ContourLoadBalancerService string
|
||||
GlooNamespace string
|
||||
SkipperRouteGroupVersion string
|
||||
Sources []string
|
||||
Namespace string
|
||||
AnnotationFilter string
|
||||
LabelFilter string
|
||||
IngressClassNames []string
|
||||
FQDNTemplate string
|
||||
CombineFQDNAndAnnotation bool
|
||||
IgnoreHostnameAnnotation bool
|
||||
IgnoreIngressTLSSpec bool
|
||||
IgnoreIngressRulesSpec bool
|
||||
GatewayNamespace string
|
||||
GatewayLabelFilter string
|
||||
Compatibility string
|
||||
PublishInternal bool
|
||||
PublishHostIP bool
|
||||
AlwaysPublishNotReadyAddresses bool
|
||||
ConnectorSourceServer string
|
||||
Provider string
|
||||
GoogleProject string
|
||||
GoogleBatchChangeSize int
|
||||
GoogleBatchChangeInterval time.Duration
|
||||
GoogleZoneVisibility string
|
||||
DomainFilter []string
|
||||
ExcludeDomains []string
|
||||
RegexDomainFilter *regexp.Regexp
|
||||
RegexDomainExclusion *regexp.Regexp
|
||||
ZoneNameFilter []string
|
||||
ZoneIDFilter []string
|
||||
TargetNetFilter []string
|
||||
ExcludeTargetNets []string
|
||||
AlibabaCloudConfigFile string
|
||||
AlibabaCloudZoneType string
|
||||
AWSZoneType string
|
||||
AWSZoneTagFilter []string
|
||||
AWSAssumeRole string
|
||||
AWSAssumeRoleExternalID string
|
||||
AWSBatchChangeSize int
|
||||
AWSBatchChangeInterval time.Duration
|
||||
AWSEvaluateTargetHealth bool
|
||||
AWSAPIRetries int
|
||||
AWSPreferCNAME bool
|
||||
AWSZoneCacheDuration time.Duration
|
||||
AWSSDServiceCleanup bool
|
||||
AzureConfigFile string
|
||||
AzureResourceGroup string
|
||||
AzureSubscriptionID string
|
||||
AzureUserAssignedIdentityClientID string
|
||||
BluecatDNSConfiguration string
|
||||
BluecatConfigFile string
|
||||
BluecatDNSView string
|
||||
BluecatGatewayHost string
|
||||
BluecatRootZone string
|
||||
BluecatDNSServerName string
|
||||
BluecatDNSDeployType string
|
||||
BluecatSkipTLSVerify bool
|
||||
CloudflareProxied bool
|
||||
CloudflareDNSRecordsPerPage int
|
||||
CoreDNSPrefix string
|
||||
RcodezeroTXTEncrypt bool
|
||||
AkamaiServiceConsumerDomain string
|
||||
AkamaiClientToken string
|
||||
AkamaiClientSecret string
|
||||
AkamaiAccessToken string
|
||||
AkamaiEdgercPath string
|
||||
AkamaiEdgercSection string
|
||||
InfobloxGridHost string
|
||||
InfobloxWapiPort int
|
||||
InfobloxWapiUsername string
|
||||
InfobloxWapiPassword string `secure:"yes"`
|
||||
InfobloxWapiVersion string
|
||||
InfobloxSSLVerify bool
|
||||
InfobloxView string
|
||||
InfobloxMaxResults int
|
||||
InfobloxFQDNRegEx string
|
||||
InfobloxNameRegEx string
|
||||
InfobloxCreatePTR bool
|
||||
InfobloxCacheDuration int
|
||||
DynCustomerName string
|
||||
DynUsername string
|
||||
DynPassword string `secure:"yes"`
|
||||
DynMinTTLSeconds int
|
||||
OCIConfigFile string
|
||||
OCICompartmentOCID string
|
||||
OCIAuthInstancePrincipal bool
|
||||
InMemoryZones []string
|
||||
OVHEndpoint string
|
||||
OVHApiRateLimit int
|
||||
PDNSServer string
|
||||
PDNSAPIKey string `secure:"yes"`
|
||||
PDNSTLSEnabled bool
|
||||
TLSCA string
|
||||
TLSClientCert string
|
||||
TLSClientCertKey string
|
||||
Policy string
|
||||
Registry string
|
||||
TXTOwnerID string
|
||||
TXTPrefix string
|
||||
TXTSuffix string
|
||||
TXTEncryptEnabled bool
|
||||
TXTEncryptAESKey string
|
||||
Interval time.Duration
|
||||
MinEventSyncInterval time.Duration
|
||||
Once bool
|
||||
DryRun bool
|
||||
UpdateEvents bool
|
||||
LogFormat string
|
||||
MetricsAddress string
|
||||
LogLevel string
|
||||
TXTCacheInterval time.Duration
|
||||
TXTWildcardReplacement string
|
||||
ExoscaleEndpoint string
|
||||
ExoscaleAPIKey string `secure:"yes"`
|
||||
ExoscaleAPISecret string `secure:"yes"`
|
||||
CRDSourceAPIVersion string
|
||||
CRDSourceKind string
|
||||
ServiceTypeFilter []string
|
||||
CFAPIEndpoint string
|
||||
CFUsername string
|
||||
CFPassword string
|
||||
ResolveServiceLoadBalancerHostname bool
|
||||
RFC2136Host string
|
||||
RFC2136Port int
|
||||
RFC2136Zone string
|
||||
RFC2136Insecure bool
|
||||
RFC2136GSSTSIG bool
|
||||
RFC2136KerberosRealm string
|
||||
RFC2136KerberosUsername string
|
||||
RFC2136KerberosPassword string `secure:"yes"`
|
||||
RFC2136TSIGKeyName string
|
||||
RFC2136TSIGSecret string `secure:"yes"`
|
||||
RFC2136TSIGSecretAlg string
|
||||
RFC2136TAXFR bool
|
||||
RFC2136MinTTL time.Duration
|
||||
RFC2136BatchChangeSize int
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
NS1MinTTLSeconds int
|
||||
TransIPAccountName string
|
||||
TransIPPrivateKeyFile string
|
||||
DigitalOceanAPIPageSize int
|
||||
ManagedDNSRecordTypes []string
|
||||
GoDaddyAPIKey string `secure:"yes"`
|
||||
GoDaddySecretKey string `secure:"yes"`
|
||||
GoDaddyTTL int64
|
||||
GoDaddyOTE bool
|
||||
OCPRouterName string
|
||||
IBMCloudProxied bool
|
||||
IBMCloudConfigFile string
|
||||
TencentCloudConfigFile string
|
||||
TencentCloudZoneType string
|
||||
PiholeServer string
|
||||
PiholePassword string `secure:"yes"`
|
||||
PiholeTLSInsecureSkipVerify bool
|
||||
PluralCluster string
|
||||
PluralProvider string
|
||||
}
|
||||
|
||||
var defaultConfig = &Config{
|
||||
@ -213,6 +219,7 @@ var defaultConfig = &Config{
|
||||
Namespace: "",
|
||||
AnnotationFilter: "",
|
||||
LabelFilter: labels.Everything().String(),
|
||||
IngressClassNames: nil,
|
||||
FQDNTemplate: "",
|
||||
CombineFQDNAndAnnotation: false,
|
||||
IgnoreHostnameAnnotation: false,
|
||||
@ -292,6 +299,8 @@ var defaultConfig = &Config{
|
||||
TXTCacheInterval: 0,
|
||||
TXTWildcardReplacement: "",
|
||||
MinEventSyncInterval: 5 * time.Second,
|
||||
TXTEncryptEnabled: false,
|
||||
TXTEncryptAESKey: "",
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
DryRun: false,
|
||||
@ -327,7 +336,7 @@ var defaultConfig = &Config{
|
||||
TransIPAccountName: "",
|
||||
TransIPPrivateKeyFile: "",
|
||||
DigitalOceanAPIPageSize: 50,
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
GoDaddyAPIKey: "",
|
||||
GoDaddySecretKey: "",
|
||||
GoDaddyTTL: 600,
|
||||
@ -388,6 +397,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("server", "The Kubernetes API server to connect to (default: auto-detect)").Default(defaultConfig.APIServerURL).StringVar(&cfg.APIServerURL)
|
||||
app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig)
|
||||
app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout)
|
||||
app.Flag("resolve-service-load-balancer-hostname", "Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs").BoolVar(&cfg.ResolveServiceLoadBalancerHostname)
|
||||
|
||||
// Flags related to cloud foundry
|
||||
app.Flag("cf-api-endpoint", "The fully-qualified domain name of the cloud foundry instance you are targeting").Default(defaultConfig.CFAPIEndpoint).StringVar(&cfg.CFAPIEndpoint)
|
||||
@ -409,6 +419,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
|
||||
app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
|
||||
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently supported by source types CRD, ingress, service and openshift-route").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
||||
app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames)
|
||||
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
|
||||
app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation)
|
||||
app.Flag("ignore-hostname-annotation", "Ignore hostname annotation when generating DNS names, valid only when using fqdn-template is set (optional, default: false)").BoolVar(&cfg.IgnoreHostnameAnnotation)
|
||||
@ -424,7 +435,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion)
|
||||
app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind)
|
||||
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
|
||||
app.Flag("managed-record-types", "Comma separated list of record types to manage (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
|
||||
app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: CNAME, A, AAAA, NS").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
|
||||
app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets)
|
||||
app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter)
|
||||
app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets)
|
||||
@ -498,6 +509,8 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("dyn-password", "When using the Dyn provider, specify the password").Default("").StringVar(&cfg.DynPassword)
|
||||
app.Flag("dyn-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.DynMinTTLSeconds)
|
||||
app.Flag("oci-config-file", "When using the OCI provider, specify the OCI configuration file (required when --provider=oci").Default(defaultConfig.OCIConfigFile).StringVar(&cfg.OCIConfigFile)
|
||||
app.Flag("oci-compartment-ocid", "When using the OCI provider, specify the OCID of the OCI compartment containing all managed zones and records. Required when using OCI IAM instance principal authentication.").StringVar(&cfg.OCICompartmentOCID)
|
||||
app.Flag("oci-auth-instance-principal", "When using the OCI provider, specify whether OCI IAM instance principal authentication should be used (instead of key-based auth via the OCI config file).").Default(strconv.FormatBool(defaultConfig.OCIAuthInstancePrincipal)).BoolVar(&cfg.OCIAuthInstancePrincipal)
|
||||
app.Flag("rcodezero-txt-encrypt", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").Default(strconv.FormatBool(defaultConfig.RcodezeroTXTEncrypt)).BoolVar(&cfg.RcodezeroTXTEncrypt)
|
||||
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)
|
||||
app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint)
|
||||
@ -564,6 +577,8 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("txt-prefix", "When using the TXT registry, a custom string that's prefixed to each ownership DNS record (optional). Could contain record type template like '%{record_type}-prefix-'. Mutual exclusive with txt-suffix!").Default(defaultConfig.TXTPrefix).StringVar(&cfg.TXTPrefix)
|
||||
app.Flag("txt-suffix", "When using the TXT registry, a custom string that's suffixed to the host portion of each ownership DNS record (optional). Could contain record type template like '-%{record_type}-suffix'. Mutual exclusive with txt-prefix!").Default(defaultConfig.TXTSuffix).StringVar(&cfg.TXTSuffix)
|
||||
app.Flag("txt-wildcard-replacement", "When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional)").Default(defaultConfig.TXTWildcardReplacement).StringVar(&cfg.TXTWildcardReplacement)
|
||||
app.Flag("txt-encrypt-enabled", "When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled)").BoolVar(&cfg.TXTEncryptEnabled)
|
||||
app.Flag("txt-encrypt-aes-key", "When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true)").Default(defaultConfig.TXTEncryptAESKey).StringVar(&cfg.TXTEncryptAESKey)
|
||||
|
||||
// Flags related to the main control loop
|
||||
app.Flag("txt-cache-interval", "The interval between cache synchronizations in duration format (default: disabled)").Default(defaultConfig.TXTCacheInterval.String()).DurationVar(&cfg.TXTCacheInterval)
|
||||
|
@ -122,7 +122,7 @@ var (
|
||||
TransIPAccountName: "",
|
||||
TransIPPrivateKeyFile: "",
|
||||
DigitalOceanAPIPageSize: 50,
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
RFC2136BatchChangeSize: 50,
|
||||
OCPRouterName: "default",
|
||||
IBMCloudProxied: false,
|
||||
@ -233,7 +233,7 @@ var (
|
||||
TransIPAccountName: "transip",
|
||||
TransIPPrivateKeyFile: "/path/to/transip.key",
|
||||
DigitalOceanAPIPageSize: 100,
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS},
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS},
|
||||
RFC2136BatchChangeSize: 100,
|
||||
IBMCloudProxied: true,
|
||||
IBMCloudConfigFile: "ibmcloud.json",
|
||||
@ -372,6 +372,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"--transip-keyfile=/path/to/transip.key",
|
||||
"--digitalocean-api-page-size=100",
|
||||
"--managed-record-types=A",
|
||||
"--managed-record-types=AAAA",
|
||||
"--managed-record-types=CNAME",
|
||||
"--managed-record-types=NS",
|
||||
"--rfc2136-batch-change-size=100",
|
||||
@ -488,7 +489,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip",
|
||||
"EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key",
|
||||
"EXTERNAL_DNS_DIGITALOCEAN_API_PAGE_SIZE": "100",
|
||||
"EXTERNAL_DNS_MANAGED_RECORD_TYPES": "A\nCNAME\nNS",
|
||||
"EXTERNAL_DNS_MANAGED_RECORD_TYPES": "A\nAAAA\nCNAME\nNS",
|
||||
"EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100",
|
||||
"EXTERNAL_DNS_IBMCLOUD_PROXIED": "1",
|
||||
"EXTERNAL_DNS_IBMCLOUD_CONFIG_FILE": "ibmcloud.json",
|
||||
|
77
plan/plan.go
77
plan/plan.go
@ -64,8 +64,15 @@ type Changes struct {
|
||||
Delete []*endpoint.Endpoint
|
||||
}
|
||||
|
||||
// planKey is a key for a row in `planTable`.
|
||||
type planKey struct {
|
||||
dnsName string
|
||||
setIdentifier string
|
||||
recordType string
|
||||
}
|
||||
|
||||
// planTable is a supplementary struct for Plan
|
||||
// each row correspond to a dnsName -> (current record + all desired records)
|
||||
// each row correspond to a planKey -> (current record + all desired records)
|
||||
/*
|
||||
planTable: (-> = target)
|
||||
--------------------------------------------------------
|
||||
@ -78,12 +85,12 @@ bar.com | | [->191.1.1.1, ->190.1.1.1] | = create (bar.com -> 1
|
||||
"=", i.e. result of calculation relies on supplied ConflictResolver
|
||||
*/
|
||||
type planTable struct {
|
||||
rows map[string]map[string]*planTableRow
|
||||
rows map[planKey]*planTableRow
|
||||
resolver ConflictResolver
|
||||
}
|
||||
|
||||
func newPlanTable() planTable { // TODO: make resolver configurable
|
||||
return planTable{map[string]map[string]*planTableRow{}, PerResource{}}
|
||||
return planTable{map[planKey]*planTableRow{}, PerResource{}}
|
||||
}
|
||||
|
||||
// planTableRow
|
||||
@ -99,25 +106,25 @@ func (t planTableRow) String() string {
|
||||
}
|
||||
|
||||
func (t planTable) addCurrent(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
}
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier].current = e
|
||||
key := t.newPlanKey(e)
|
||||
t.rows[key].current = e
|
||||
}
|
||||
|
||||
func (t planTable) addCandidate(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
key := t.newPlanKey(e)
|
||||
t.rows[key].candidates = append(t.rows[key].candidates, e)
|
||||
}
|
||||
|
||||
func (t *planTable) newPlanKey(e *endpoint.Endpoint) planKey {
|
||||
key := planKey{
|
||||
dnsName: normalizeDNSName(e.DNSName),
|
||||
setIdentifier: e.SetIdentifier,
|
||||
recordType: e.RecordType,
|
||||
}
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
if _, ok := t.rows[key]; !ok {
|
||||
t.rows[key] = &planTableRow{}
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier].candidates = append(t.rows[dnsName][e.SetIdentifier].candidates, e)
|
||||
return key
|
||||
}
|
||||
|
||||
func (c *Changes) HasChanges() bool {
|
||||
@ -146,26 +153,24 @@ func (p *Plan) Calculate() *Plan {
|
||||
|
||||
changes := &Changes{}
|
||||
|
||||
for _, topRow := range t.rows {
|
||||
for _, row := range topRow {
|
||||
if row.current == nil { // dns name not taken
|
||||
changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
changes.Delete = append(changes.Delete, row.current)
|
||||
}
|
||||
for _, row := range t.rows {
|
||||
if row.current == nil { // dns name not taken
|
||||
changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
changes.Delete = append(changes.Delete, row.current)
|
||||
}
|
||||
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
if row.current != nil && len(row.candidates) > 0 { // dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
changes.UpdateNew = append(changes.UpdateNew, update)
|
||||
changes.UpdateOld = append(changes.UpdateOld, row.current)
|
||||
}
|
||||
continue
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
if row.current != nil && len(row.candidates) > 0 { // dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
changes.UpdateNew = append(changes.UpdateNew, update)
|
||||
changes.UpdateOld = append(changes.UpdateOld, row.current)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, pol := range p.Policies {
|
||||
@ -181,7 +186,7 @@ func (p *Plan) Calculate() *Plan {
|
||||
Current: p.Current,
|
||||
Desired: p.Desired,
|
||||
Changes: changes,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
return plan
|
||||
|
@ -35,6 +35,9 @@ type PlanTestSuite struct {
|
||||
fooV2CnameNoLabel *endpoint.Endpoint
|
||||
fooV3CnameSameResource *endpoint.Endpoint
|
||||
fooA5 *endpoint.Endpoint
|
||||
fooAAAA *endpoint.Endpoint
|
||||
dsA *endpoint.Endpoint
|
||||
dsAAAA *endpoint.Endpoint
|
||||
bar127A *endpoint.Endpoint
|
||||
bar127AWithTTL *endpoint.Endpoint
|
||||
bar127AWithProviderSpecificTrue *endpoint.Endpoint
|
||||
@ -106,6 +109,30 @@ func (suite *PlanTestSuite) SetupTest() {
|
||||
endpoint.ResourceLabelKey: "ingress/default/foo-5",
|
||||
},
|
||||
}
|
||||
suite.fooAAAA = &endpoint.Endpoint{
|
||||
DNSName: "foo",
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
RecordType: "AAAA",
|
||||
Labels: map[string]string{
|
||||
endpoint.ResourceLabelKey: "ingress/default/foo-AAAA",
|
||||
},
|
||||
}
|
||||
suite.dsA = &endpoint.Endpoint{
|
||||
DNSName: "ds",
|
||||
Targets: endpoint.Targets{"1.1.1.1"},
|
||||
RecordType: "A",
|
||||
Labels: map[string]string{
|
||||
endpoint.ResourceLabelKey: "ingress/default/ds",
|
||||
},
|
||||
}
|
||||
suite.dsAAAA = &endpoint.Endpoint{
|
||||
DNSName: "ds",
|
||||
Targets: endpoint.Targets{"1.1.1.1"},
|
||||
RecordType: "AAAA",
|
||||
Labels: map[string]string{
|
||||
endpoint.ResourceLabelKey: "ingress/default/ds-AAAAA",
|
||||
},
|
||||
}
|
||||
suite.bar127A = &endpoint.Endpoint{
|
||||
DNSName: "bar",
|
||||
Targets: endpoint.Targets{"127.0.0.1"},
|
||||
@ -438,9 +465,9 @@ func (suite *PlanTestSuite) TestIdempotency() {
|
||||
func (suite *PlanTestSuite) TestDifferentTypes() {
|
||||
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooA5}
|
||||
expectedCreate := []*endpoint.Endpoint{}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.fooA5}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooA5}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooV2Cname}
|
||||
expectedDelete := []*endpoint.Endpoint{}
|
||||
|
||||
p := &Plan{
|
||||
@ -544,52 +571,6 @@ func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() {
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
// TODO: remove once multiple-target per endpoint is supported
|
||||
func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() {
|
||||
current := []*endpoint.Endpoint{suite.fooV3CnameSameResource, suite.bar192A}
|
||||
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource}
|
||||
expectedCreate := []*endpoint.Endpoint{}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV3CnameSameResource}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
expectedDelete := []*endpoint.Endpoint{suite.bar192A}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew)
|
||||
validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld)
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
// TODO: remove once multiple-target per endpoint is supported
|
||||
func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() {
|
||||
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A}
|
||||
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource}
|
||||
expectedCreate := []*endpoint.Endpoint{}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{}
|
||||
expectedDelete := []*endpoint.Endpoint{suite.bar192A}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew)
|
||||
validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld)
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() {
|
||||
current := []*endpoint.Endpoint{suite.multiple1}
|
||||
desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3}
|
||||
@ -695,6 +676,39 @@ func (suite *PlanTestSuite) TestMissing() {
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestAAAARecords() {
|
||||
|
||||
current := []*endpoint.Endpoint{}
|
||||
desired := []*endpoint.Endpoint{suite.fooAAAA}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.fooAAAA}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
ManagedRecords: []string{endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestDualStackRecords() {
|
||||
current := []*endpoint.Endpoint{}
|
||||
desired := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
}
|
||||
|
||||
func TestPlan(t *testing.T) {
|
||||
suite.Run(t, new(PlanTestSuite))
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ var canonicalHostedZones = map[string]string{
|
||||
"eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4",
|
||||
"eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO",
|
||||
"eu-south-1.elb.amazonaws.com": "Z3ULH7SSC9OV64",
|
||||
"eu-south-2.elb.amazonaws.com": "Z0956581394HF5D5LXGAP",
|
||||
"sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU",
|
||||
"cn-north-1.elb.amazonaws.com.cn": "Z1GDH35T77C1KE",
|
||||
"cn-northwest-1.elb.amazonaws.com.cn": "ZM7IZAIOVVDZF",
|
||||
@ -115,6 +116,7 @@ var canonicalHostedZones = map[string]string{
|
||||
"elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5",
|
||||
"elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM",
|
||||
"elb.eu-south-1.amazonaws.com": "Z23146JA1KNAFP",
|
||||
"elb.eu-south-2.amazonaws.com": "Z1011216NVTVYADP1SSV",
|
||||
"elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU",
|
||||
"elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6",
|
||||
"elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D",
|
||||
@ -373,7 +375,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*route53.Hos
|
||||
for _, r := range resp.ResourceRecordSets {
|
||||
newEndpoints := make([]*endpoint.Endpoint, 0)
|
||||
|
||||
if !provider.SupportedRecordType(aws.StringValue(r.Type)) {
|
||||
if !p.SupportedRecordType(aws.StringValue(r.Type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1059,3 +1061,12 @@ func canonicalHostedZone(hostname string) string {
|
||||
func cleanZoneID(id string) string {
|
||||
return strings.TrimPrefix(id, "/hostedzone/")
|
||||
}
|
||||
|
||||
func (p *AWSProvider) SupportedRecordType(recordType string) bool {
|
||||
switch recordType {
|
||||
case "MX":
|
||||
return true
|
||||
default:
|
||||
return provider.SupportedRecordType(recordType)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -509,7 +509,7 @@ func (p *AWSSDProvider) DeleteService(service *sd.Service) error {
|
||||
// convert ownerID string to service description format
|
||||
label := endpoint.NewLabels()
|
||||
label[endpoint.OwnerLabelKey] = p.ownerID
|
||||
label[endpoint.AWSSDDescriptionLabel] = label.Serialize(false)
|
||||
label[endpoint.AWSSDDescriptionLabel] = label.SerializePlain(false)
|
||||
|
||||
if strings.HasPrefix(aws.StringValue(service.Description), label[endpoint.AWSSDDescriptionLabel]) {
|
||||
log.Infof("Deleting service \"%s\"", *service.Name)
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// nolint:staticcheck
|
||||
//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go
|
||||
package azure
|
||||
|
||||
import (
|
||||
@ -109,7 +109,7 @@ func (p *AzureProvider) Records(ctx context.Context) (endpoints []*endpoint.Endp
|
||||
return true
|
||||
}
|
||||
recordType := strings.TrimPrefix(*recordSet.Type, "Microsoft.Network/dnszones/")
|
||||
if !provider.SupportedRecordType(recordType) {
|
||||
if !p.SupportedRecordType(recordType) {
|
||||
return true
|
||||
}
|
||||
name := formatAzureDNSName(*recordSet.Name, *zone.Name)
|
||||
@ -190,6 +190,15 @@ func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) {
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func (p *AzureProvider) SupportedRecordType(recordType string) bool {
|
||||
switch recordType {
|
||||
case "MX":
|
||||
return true
|
||||
default:
|
||||
return provider.SupportedRecordType(recordType)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AzureProvider) iterateRecords(ctx context.Context, zoneName string, callback func(dns.RecordSet) bool) error {
|
||||
log.Debugf("Retrieving Azure DNS records for zone '%s'.", zoneName)
|
||||
|
||||
@ -241,10 +250,6 @@ func (p *AzureProvider) mapChanges(zones []dns.Zone, changes *plan.Changes) (azu
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.UpdateOld {
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.Create {
|
||||
mapChange(updated, change)
|
||||
}
|
||||
@ -377,6 +382,21 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case dns.MX:
|
||||
mxRecords := make([]dns.MxRecord, len(endpoint.Targets))
|
||||
for i, target := range endpoint.Targets {
|
||||
mxRecord, err := parseMxTarget[dns.MxRecord](target)
|
||||
if err != nil {
|
||||
return dns.RecordSet{}, err
|
||||
}
|
||||
mxRecords[i] = mxRecord
|
||||
}
|
||||
return dns.RecordSet{
|
||||
RecordSetProperties: &dns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
MxRecords: &mxRecords,
|
||||
},
|
||||
}, nil
|
||||
case dns.TXT:
|
||||
return dns.RecordSet{
|
||||
RecordSetProperties: &dns.RecordSetProperties{
|
||||
@ -425,6 +445,16 @@ func extractAzureTargets(recordSet *dns.RecordSet) []string {
|
||||
return []string{*cnameRecord.Cname}
|
||||
}
|
||||
|
||||
// Check for MX records
|
||||
mxRecords := properties.MxRecords
|
||||
if mxRecords != nil && len(*mxRecords) > 0 && (*mxRecords)[0].Exchange != nil {
|
||||
targets := make([]string, len(*mxRecords))
|
||||
for i, mxRecord := range *mxRecords {
|
||||
targets[i] = fmt.Sprintf("%d %s", *mxRecord.Preference, *mxRecord.Exchange)
|
||||
}
|
||||
return targets
|
||||
}
|
||||
|
||||
// Check for TXT records
|
||||
txtRecords := properties.TxtRecords
|
||||
if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil {
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// nolint:staticcheck
|
||||
//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go
|
||||
package azure
|
||||
|
||||
import (
|
||||
@ -237,10 +237,6 @@ func (p *AzurePrivateDNSProvider) mapChanges(zones []privatedns.PrivateZone, cha
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.UpdateOld {
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.Create {
|
||||
mapChange(updated, change)
|
||||
}
|
||||
@ -367,6 +363,21 @@ func (p *AzurePrivateDNSProvider) newRecordSet(endpoint *endpoint.Endpoint) (pri
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case privatedns.MX:
|
||||
mxRecords := make([]privatedns.MxRecord, len(endpoint.Targets))
|
||||
for i, target := range endpoint.Targets {
|
||||
mxRecord, err := parseMxTarget[privatedns.MxRecord](target)
|
||||
if err != nil {
|
||||
return privatedns.RecordSet{}, err
|
||||
}
|
||||
mxRecords[i] = mxRecord
|
||||
}
|
||||
return privatedns.RecordSet{
|
||||
RecordSetProperties: &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
MxRecords: &mxRecords,
|
||||
},
|
||||
}, nil
|
||||
case privatedns.TXT:
|
||||
return privatedns.RecordSet{
|
||||
RecordSetProperties: &privatedns.RecordSetProperties{
|
||||
@ -407,6 +418,16 @@ func extractAzurePrivateDNSTargets(recordSet *privatedns.RecordSet) []string {
|
||||
return []string{*cnameRecord.Cname}
|
||||
}
|
||||
|
||||
// Check for MX records
|
||||
mxRecords := properties.MxRecords
|
||||
if mxRecords != nil && len(*mxRecords) > 0 && (*mxRecords)[0].Exchange != nil {
|
||||
targets := make([]string, len(*mxRecords))
|
||||
for i, mxRecord := range *mxRecords {
|
||||
targets[i] = fmt.Sprintf("%d %s", *mxRecord.Preference, *mxRecord.Exchange)
|
||||
}
|
||||
return targets
|
||||
}
|
||||
|
||||
// Check for TXT records
|
||||
txtRecords := properties.TxtRecords
|
||||
if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil {
|
||||
|
@ -123,6 +123,17 @@ func privateCNameRecordSetPropertiesGetter(values []string, ttl int64) *privated
|
||||
}
|
||||
}
|
||||
|
||||
func privateMXRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
mxRecords := make([]privatedns.MxRecord, len(values))
|
||||
for i, target := range values {
|
||||
mxRecords[i], _ = parseMxTarget[privatedns.MxRecord](target)
|
||||
}
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
MxRecords: &mxRecords,
|
||||
}
|
||||
}
|
||||
|
||||
func privateTxtRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
@ -156,6 +167,8 @@ func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64,
|
||||
getterFunc = privateARecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeCNAME:
|
||||
getterFunc = privateCNameRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeMX:
|
||||
getterFunc = privateMXRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeTXT:
|
||||
getterFunc = privateTxtRecordSetPropertiesGetter
|
||||
default:
|
||||
@ -266,6 +279,7 @@ func TestAzurePrivateDNSRecord(t *testing.T) {
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
createPrivateMockRecordSetWithTTL("mail", endpoint.RecordTypeMX, "10 example.com", 4000),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -281,6 +295,7 @@ func TestAzurePrivateDNSRecord(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
@ -299,6 +314,7 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) {
|
||||
createPrivateMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
createPrivateMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -314,6 +330,7 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
@ -325,8 +342,6 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) {
|
||||
testAzurePrivateDNSApplyChangesInternal(t, false, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
})
|
||||
@ -342,6 +357,9 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -401,17 +419,21 @@ func testAzurePrivateDNSApplyChangesInternal(t *testing.T, dryRun bool, client P
|
||||
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"),
|
||||
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
|
||||
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
|
@ -122,6 +122,17 @@ func cNameRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetPr
|
||||
}
|
||||
}
|
||||
|
||||
func mxRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties {
|
||||
mxRecords := make([]dns.MxRecord, len(values))
|
||||
for i, target := range values {
|
||||
mxRecords[i], _ = parseMxTarget[dns.MxRecord](target)
|
||||
}
|
||||
return &dns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
MxRecords: &mxRecords,
|
||||
}
|
||||
}
|
||||
|
||||
func txtRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties {
|
||||
return &dns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
@ -155,6 +166,8 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values
|
||||
getterFunc = aRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeCNAME:
|
||||
getterFunc = cNameRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeMX:
|
||||
getterFunc = mxRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeTXT:
|
||||
getterFunc = txtRecordSetPropertiesGetter
|
||||
default:
|
||||
@ -271,6 +284,7 @@ func TestAzureRecord(t *testing.T) {
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -287,6 +301,7 @@ func TestAzureRecord(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
@ -305,6 +320,7 @@ func TestAzureMultiRecord(t *testing.T) {
|
||||
createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -321,6 +337,7 @@ func TestAzureMultiRecord(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
@ -332,8 +349,6 @@ func TestAzureApplyChanges(t *testing.T) {
|
||||
testAzureApplyChangesInternal(t, false, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
})
|
||||
@ -349,6 +364,9 @@ func TestAzureApplyChanges(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"),
|
||||
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -410,17 +428,21 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
|
||||
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"),
|
||||
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
|
||||
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
@ -455,6 +477,7 @@ func TestAzureNameFilter(t *testing.T) {
|
||||
createMockRecordSetWithTTL("test.nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
})
|
||||
if err != nil {
|
||||
@ -470,6 +493,7 @@ func TestAzureNameFilter(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("test.nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("mail.nginx.example.com", endpoint.RecordTypeMX, recordTTL, "20 example.com"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
@ -481,8 +505,6 @@ func TestAzureApplyChangesZoneName(t *testing.T) {
|
||||
testAzureApplyChangesInternalZoneName(t, false, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.foo.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("oldcname.foo.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
})
|
||||
|
47
provider/azure/common.go
Normal file
47
provider/azure/common.go
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go
|
||||
package azure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
|
||||
"github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
)
|
||||
|
||||
// Helper function (shared with test code)
|
||||
func parseMxTarget[T dns.MxRecord | privatedns.MxRecord](mxTarget string) (T, error) {
|
||||
targetParts := strings.SplitN(mxTarget, " ", 2)
|
||||
if len(targetParts) != 2 {
|
||||
return T{}, fmt.Errorf("mx target needs to be of form '10 example.com'")
|
||||
}
|
||||
|
||||
preferenceRaw, exchange := targetParts[0], targetParts[1]
|
||||
preference, err := strconv.ParseInt(preferenceRaw, 10, 32)
|
||||
if err != nil {
|
||||
return T{}, fmt.Errorf("invalid preference specified")
|
||||
}
|
||||
|
||||
return T{
|
||||
Preference: to.Int32Ptr(int32(preference)),
|
||||
Exchange: to.StringPtr(exchange),
|
||||
}, nil
|
||||
}
|
87
provider/azure/common_test.go
Normal file
87
provider/azure/common_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
|
||||
"github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_parseMxTarget(t *testing.T) {
|
||||
type testCase[T interface {
|
||||
dns.MxRecord | privatedns.MxRecord
|
||||
}] struct {
|
||||
name string
|
||||
args string
|
||||
want T
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}
|
||||
|
||||
tests := []testCase[dns.MxRecord]{
|
||||
{
|
||||
name: "valid mx target",
|
||||
args: "10 example.com",
|
||||
want: dns.MxRecord{
|
||||
Preference: to.Int32Ptr(int32(10)),
|
||||
Exchange: to.StringPtr("example.com"),
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "valid mx target with a subdomain",
|
||||
args: "99 foo-bar.example.com",
|
||||
want: dns.MxRecord{
|
||||
Preference: to.Int32Ptr(int32(99)),
|
||||
Exchange: to.StringPtr("foo-bar.example.com"),
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "invalid mx target with misplaced preference and exchange",
|
||||
args: "example.com 10",
|
||||
want: dns.MxRecord{},
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "invalid mx target without preference",
|
||||
args: "example.com",
|
||||
want: dns.MxRecord{},
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "invalid mx target with non numeric preference",
|
||||
args: "aa example.com",
|
||||
want: dns.MxRecord{},
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseMxTarget[dns.MxRecord](tt.args)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("parseMxTarget(%v)", tt.args)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "parseMxTarget(%v)", tt.args)
|
||||
})
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
cloudflare "github.com/cloudflare/cloudflare-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -155,7 +156,15 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
|
||||
err error
|
||||
)
|
||||
if os.Getenv("CF_API_TOKEN") != "" {
|
||||
config, err = cloudflare.NewWithAPIToken(os.Getenv("CF_API_TOKEN"))
|
||||
token := os.Getenv("CF_API_TOKEN")
|
||||
if strings.HasPrefix(token, "file:") {
|
||||
tokenBytes, err := os.ReadFile(strings.TrimPrefix(token, "file:"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CF_API_TOKEN from file: %w", err)
|
||||
}
|
||||
token = string(tokenBytes)
|
||||
}
|
||||
config, err = cloudflare.NewWithAPIToken(token)
|
||||
} else {
|
||||
config, err = cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
|
||||
}
|
||||
|
@ -677,6 +677,23 @@ func TestCloudflareProvider(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
|
||||
_ = os.Unsetenv("CF_API_TOKEN")
|
||||
tokenFile := "/tmp/cf_api_token"
|
||||
if err := os.WriteFile(tokenFile, []byte("abc123def"), 0644); err != nil {
|
||||
t.Errorf("failed to write token file, %s", err)
|
||||
}
|
||||
_ = os.Setenv("CF_API_TOKEN", tokenFile)
|
||||
_, err = NewCloudFlareProvider(
|
||||
endpoint.NewDomainFilter([]string{"bar.com"}),
|
||||
provider.NewZoneIDFilter([]string{""}),
|
||||
false,
|
||||
true,
|
||||
5000)
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
|
||||
_ = os.Unsetenv("CF_API_TOKEN")
|
||||
_ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx")
|
||||
_ = os.Setenv("CF_API_EMAIL", "test@test.com")
|
||||
@ -689,6 +706,7 @@ func TestCloudflareProvider(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
|
||||
_ = os.Unsetenv("CF_API_KEY")
|
||||
_ = os.Unsetenv("CF_API_EMAIL")
|
||||
_, err = NewCloudFlareProvider(
|
||||
|
@ -322,13 +322,13 @@ func (p designateProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, e
|
||||
if recordSet.Type != endpoint.RecordTypeA && recordSet.Type != endpoint.RecordTypeTXT && recordSet.Type != endpoint.RecordTypeCNAME {
|
||||
return nil
|
||||
}
|
||||
for _, record := range recordSet.Records {
|
||||
ep := endpoint.NewEndpoint(recordSet.Name, recordSet.Type, record)
|
||||
ep.Labels[designateRecordSetID] = recordSet.ID
|
||||
ep.Labels[designateZoneID] = recordSet.ZoneID
|
||||
ep.Labels[designateOriginalRecords] = strings.Join(recordSet.Records, "\000")
|
||||
result = append(result, ep)
|
||||
}
|
||||
|
||||
ep := endpoint.NewEndpoint(recordSet.Name, recordSet.Type, recordSet.Records...)
|
||||
ep.Labels[designateRecordSetID] = recordSet.ID
|
||||
ep.Labels[designateZoneID] = recordSet.ZoneID
|
||||
ep.Labels[designateOriginalRecords] = strings.Join(recordSet.Records, "\000")
|
||||
result = append(result, ep)
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
@ -358,7 +358,7 @@ type recordSet struct {
|
||||
}
|
||||
|
||||
// adds endpoint into recordset aggregation, loading original values from endpoint labels first
|
||||
func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete bool) {
|
||||
func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, oldEndpoints []*endpoint.Endpoint, delete bool) {
|
||||
key := fmt.Sprintf("%s/%s", ep.DNSName, ep.RecordType)
|
||||
rs := recordSets[key]
|
||||
if rs == nil {
|
||||
@ -368,6 +368,9 @@ func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete
|
||||
names: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
addDesignateIDLabelsFromExistingEndpoints(oldEndpoints, ep)
|
||||
|
||||
if rs.zoneID == "" {
|
||||
rs.zoneID = ep.Labels[designateZoneID]
|
||||
}
|
||||
@ -389,25 +392,55 @@ func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete
|
||||
recordSets[key] = rs
|
||||
}
|
||||
|
||||
// addDesignateIDLabelsFromExistingEndpoints adds the labels identified by the constants designateZoneID and designateRecordSetID
|
||||
// to an Endpoint. Therefore, it searches all given existing endpoints for an endpoint with the same record type and record
|
||||
// value. If the given Endpoint already has the labels set, they are left untouched. This fixes an issue with the
|
||||
// TXTRegistry which generates new TXT entries instead of updating the old ones.
|
||||
func addDesignateIDLabelsFromExistingEndpoints(existingEndpoints []*endpoint.Endpoint, ep *endpoint.Endpoint) {
|
||||
_, hasZoneIDLabel := ep.Labels[designateZoneID]
|
||||
_, hasRecordSetIDLabel := ep.Labels[designateRecordSetID]
|
||||
if hasZoneIDLabel && hasRecordSetIDLabel {
|
||||
return
|
||||
}
|
||||
for _, oep := range existingEndpoints {
|
||||
if ep.RecordType == oep.RecordType && ep.DNSName == oep.DNSName {
|
||||
if !hasZoneIDLabel {
|
||||
ep.Labels[designateZoneID] = oep.Labels[designateZoneID]
|
||||
}
|
||||
if !hasRecordSetIDLabel {
|
||||
ep.Labels[designateRecordSetID] = oep.Labels[designateRecordSetID]
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyChanges applies a given set of changes in a given zone.
|
||||
func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
managedZones, err := p.getZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoints, err := p.Records(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch active records: %w", err)
|
||||
}
|
||||
|
||||
recordSets := map[string]*recordSet{}
|
||||
for _, ep := range changes.Create {
|
||||
addEndpoint(ep, recordSets, false)
|
||||
}
|
||||
for _, ep := range changes.UpdateNew {
|
||||
addEndpoint(ep, recordSets, false)
|
||||
addEndpoint(ep, recordSets, endpoints, false)
|
||||
}
|
||||
for _, ep := range changes.UpdateOld {
|
||||
addEndpoint(ep, recordSets, true)
|
||||
addEndpoint(ep, recordSets, endpoints, true)
|
||||
}
|
||||
for _, ep := range changes.UpdateNew {
|
||||
addEndpoint(ep, recordSets, endpoints, false)
|
||||
}
|
||||
for _, ep := range changes.Delete {
|
||||
addEndpoint(ep, recordSets, true)
|
||||
addEndpoint(ep, recordSets, endpoints, true)
|
||||
}
|
||||
|
||||
for _, rs := range recordSets {
|
||||
if err2 := p.upsertRecordSet(rs, managedZones); err == nil {
|
||||
err = err2
|
||||
|
@ -274,17 +274,7 @@ func TestDesignateRecords(t *testing.T) {
|
||||
{
|
||||
DNSName: "srv.test.net",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"10.2.1.1"},
|
||||
Labels: map[string]string{
|
||||
designateRecordSetID: rs21ID,
|
||||
designateZoneID: zone2ID,
|
||||
designateOriginalRecords: "10.2.1.1\00010.2.1.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "srv.test.net",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"10.2.1.2"},
|
||||
Targets: endpoint.Targets{"10.2.1.1", "10.2.1.2"},
|
||||
Labels: map[string]string{
|
||||
designateRecordSetID: rs21ID,
|
||||
designateZoneID: zone2ID,
|
||||
@ -336,6 +326,19 @@ func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*re
|
||||
Status: "ACTIVE",
|
||||
})
|
||||
}
|
||||
|
||||
_, err := client.CreateRecordSet("zone-1", recordsets.CreateOpts{
|
||||
Name: "www.example.com.",
|
||||
Description: "",
|
||||
Records: []string{"foo"},
|
||||
TTL: 60,
|
||||
Type: endpoint.RecordTypeTXT,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("failed to prefil records")
|
||||
}
|
||||
|
||||
endpoints := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "www.example.com",
|
||||
@ -409,7 +412,7 @@ func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*re
|
||||
expectedCopy := make([]*recordsets.RecordSet, len(expected))
|
||||
copy(expectedCopy, expected)
|
||||
|
||||
err := client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Create: endpoints})
|
||||
err = client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Create: endpoints})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user