mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Merge branch 'master' into gloo-multiple-namespaces
This commit is contained in:
commit
41d3de5364
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@ -9,11 +9,23 @@ updates:
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
dev-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
dev-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "docker" # Keep Docker dependencies up to date
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
dev-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -21,9 +21,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
apt update
|
||||
apt install -y make gcc libc-dev git
|
||||
if: github.actor == 'nektos/act'
|
||||
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -28,9 +28,9 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Install go version
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.19'
|
||||
go-version: '^1.20'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
6
.github/workflows/docs.yml
vendored
6
.github/workflows/docs.yml
vendored
@ -18,15 +18,15 @@ 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
|
||||
go-version: '^1.20'
|
||||
|
||||
- run: |
|
||||
pip install -r docs/scripts/requirements.txt
|
||||
|
4
.github/workflows/gh-workflow-approve.yaml
vendored
4
.github/workflows/gh-workflow-approve.yaml
vendored
@ -17,11 +17,11 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Update PR
|
||||
uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0 # v6.3.3
|
||||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
continue-on-error: true
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
debug: ${{ secrets.ACTIONS_RUNNER_DEBUG }}
|
||||
debug: ${{ secrets.ACTIONS_RUNNER_DEBUG == 'true' }}
|
||||
script: |
|
||||
const result = await github.rest.actions.listWorkflowRunsForRepo({
|
||||
owner: context.repo.owner,
|
||||
|
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.4.0
|
||||
with:
|
||||
comment: "true" # enable comment mode
|
||||
yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)"
|
6
.github/workflows/lint-test-chart.yaml
vendored
6
.github/workflows/lint-test-chart.yaml
vendored
@ -38,21 +38,21 @@ jobs:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Set-up chart-testing
|
||||
uses: helm/chart-testing-action@afea100a513515fbd68b0e72a7bb0ae34cb62aec
|
||||
uses: helm/chart-testing-action@e8788873172cb653a90ca2e819d79d65a66d4e76
|
||||
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed)
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "::set-output name=changed::true"
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --check-version-increment=false
|
||||
|
||||
- name: Set-up Kind cluster
|
||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00
|
||||
uses: helm/kind-action@fa81e57adff234b2908110485695db0f181f3c67
|
||||
with:
|
||||
wait: 120s
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
|
6
.github/workflows/lint.yaml
vendored
6
.github/workflows/lint.yaml
vendored
@ -21,9 +21,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@ -31,5 +31,5 @@ jobs:
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.3
|
||||
make lint
|
||||
|
2
.github/workflows/release-chart.yaml
vendored
2
.github/workflows/release-chart.yaml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chart_version="$(grep -Po "(?<=^version: ).+" charts/external-dns/Chart.yaml)"
|
||||
echo "::set-output name=version::${chart_version}"
|
||||
echo "version=${chart_version}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get changelog entry
|
||||
id: changelog_reader
|
||||
|
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
|
@ -19,7 +19,6 @@ linters:
|
||||
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
||||
disable-all: true
|
||||
enable:
|
||||
- depguard
|
||||
- dogsled
|
||||
- gofmt
|
||||
- goimports
|
||||
@ -61,6 +60,14 @@ issues:
|
||||
- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
- path: source/ambassador_host.go
|
||||
linters: [ typecheck ]
|
||||
- path: source/contour_httpproxy.go
|
||||
linters: [ typecheck ]
|
||||
- path: source/f5_virtualserver.go
|
||||
linters: [ typecheck ]
|
||||
- path: source/kong_tcpingress.go
|
||||
linters: [ typecheck ]
|
||||
|
||||
run:
|
||||
skip-files:
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
# builder image
|
||||
ARG ARCH
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20 as builder
|
||||
ARG ARCH
|
||||
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
@ -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
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20 as builder
|
||||
|
||||
WORKDIR /sigs.k8s.io/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
|
||||
|
1
OWNERS
1
OWNERS
@ -8,6 +8,7 @@ approvers:
|
||||
- szuecs
|
||||
|
||||
reviewers:
|
||||
- johngmyers
|
||||
- njuettner
|
||||
- raffo
|
||||
- seanmalloy
|
||||
|
79
README.md
79
README.md
@ -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
|
||||
@ -63,11 +63,10 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz
|
||||
* [Plural](https://www.plural.sh/)
|
||||
* [Pi-hole](https://pi-hole.net/)
|
||||
|
||||
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||
ExternalDNS is, by default, aware of the records it is managing, therefore it can safely manage non-empty hosted zones. We strongly encourage you to set `--txt-owner-id` to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||
|
||||
Note that all flags can be replaced with environment variables; for instance,
|
||||
`--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`, or
|
||||
`--registry txt` could be replaced with `EXTERNAL_DNS_REGISTRY=txt`.
|
||||
`--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`.
|
||||
|
||||
## Status of providers
|
||||
|
||||
@ -176,6 +175,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)
|
||||
@ -236,17 +236,17 @@ If the service is not of type Loadbalancer you need the --publish-internal-servi
|
||||
Locally run a single sync loop of ExternalDNS.
|
||||
|
||||
```console
|
||||
external-dns --registry txt --txt-owner-id my-cluster-id --provider google --google-project example-project --source service --once --dry-run
|
||||
external-dns --txt-owner-id my-cluster-id --provider google --google-project example-project --source service --once --dry-run
|
||||
```
|
||||
|
||||
This should output the DNS records it will modify to match the managed zone with the DNS records you desire. It also assumes you are running in the `default` namespace. See the [FAQ](docs/faq.md) for more information regarding namespaces.
|
||||
|
||||
Note: TXT records will have `my-cluster-id` value embedded. Those are used to ensure that ExternalDNS is aware of the records it manages.
|
||||
Note: TXT records will have the `my-cluster-id` value embedded. Those are used to ensure that ExternalDNS is aware of the records it manages.
|
||||
|
||||
Once you're satisfied with the result, you can run ExternalDNS like you would run it in your cluster: as a control loop, and **not in dry-run** mode:
|
||||
|
||||
```console
|
||||
external-dns --registry txt --txt-owner-id my-cluster-id --provider google --google-project example-project --source service
|
||||
external-dns --txt-owner-id my-cluster-id --provider google --google-project example-project --source service
|
||||
```
|
||||
|
||||
Check that ExternalDNS has created the desired DNS record for your Service and that it points to its load balancer's IP. Then try to resolve it:
|
||||
@ -270,71 +270,6 @@ If using a txt registry and attempting to use a CNAME the `--txt-prefix` must be
|
||||
|
||||
If `externalIPs` list is defined for a `LoadBalancer` service, this list will be used instead of an assigned load balancer IP to create a DNS record. It's useful when you run bare metal Kubernetes clusters behind NAT or in a similar setup, where a load balancer IP differs from a public IP (e.g. with [MetalLB](https://metallb.universe.tf)).
|
||||
|
||||
# Roadmap
|
||||
|
||||
ExternalDNS was built with extensibility in mind. Adding and experimenting with new DNS providers and sources of desired DNS records should be as easy as possible. It should also be possible to modify how ExternalDNS behaves—e.g. whether it should add records but never delete them.
|
||||
|
||||
Here's a rough outline on what is to come (subject to change):
|
||||
|
||||
### v0.1
|
||||
|
||||
- [x] Support for Google CloudDNS
|
||||
- [x] Support for Kubernetes Services
|
||||
|
||||
### v0.2
|
||||
|
||||
- [x] Support for AWS Route 53
|
||||
- [x] Support for Kubernetes Ingresses
|
||||
|
||||
### v0.3
|
||||
|
||||
- [x] Support for AWS Route 53 via ALIAS
|
||||
- [x] Support for multiple zones
|
||||
- [x] Ownership System
|
||||
|
||||
### v0.4
|
||||
|
||||
- [x] Support for AzureDNS
|
||||
- [x] Support for CloudFlare
|
||||
- [x] Support for DigitalOcean
|
||||
- [x] Multiple DNS names per Service
|
||||
|
||||
### v0.5
|
||||
|
||||
- [x] Support for creating DNS records to multiple targets (for Google and AWS)
|
||||
- [x] Support for OpenStack Designate
|
||||
- [x] Support for PowerDNS
|
||||
- [x] Support for Linode
|
||||
- [x] Support for RcodeZero
|
||||
- [x] Support for NS1
|
||||
- [x] Support for TransIP
|
||||
- [x] Support for Azure Private DNS
|
||||
|
||||
### v0.6
|
||||
|
||||
- [ ] Ability to replace kOps' [DNS Controller](https://github.com/kubernetes/kops/tree/HEAD/dns-controller) (This could also directly become `v1.0`)
|
||||
- [x] Support for OVH
|
||||
|
||||
### v1.0
|
||||
|
||||
- [ ] Ability to replace kOps' [DNS Controller](https://github.com/kubernetes/kops/tree/HEAD/dns-controller)
|
||||
- [x] Add support for pod source
|
||||
- [x] Add support for DNS Controller annotations for pod and service sources
|
||||
- [ ] Add support for kOps gossip provider
|
||||
- [x] Ability to replace Zalando's [Mate](https://github.com/linki/mate)
|
||||
- [x] Ability to replace Molecule Software's [route53-kubernetes](https://github.com/wearemolecule/route53-kubernetes)
|
||||
|
||||
### Yet to be defined
|
||||
|
||||
* Support for CoreDNS
|
||||
* Support for record weights
|
||||
* Support for different behavioral policies
|
||||
* Support for Services with `type=NodePort`
|
||||
* Support for CRDs
|
||||
* Support for more advanced DNS record configurations
|
||||
|
||||
Have a look at [the milestones](https://github.com/kubernetes-sigs/external-dns/milestones) to get an idea of where we currently stand.
|
||||
|
||||
## Contributing
|
||||
|
||||
Are you interested in contributing to external-dns? We, the maintainers and community, would love your
|
||||
|
@ -7,15 +7,29 @@ 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 -->
|
||||
|
||||
- Disallowed privilege escalation in container security context and set the seccomp profile type to `RuntimeDefault`. ([#3689](https://github.com/kubernetes-sigs/external-dns/pull/3689)) [@nrvnrvn](https://github.com/nrvnrvn)
|
||||
|
||||
## [v1.13.0] - 2023-03-30
|
||||
|
||||
### All Changes
|
||||
|
||||
- Updated _ExternalDNS_ version to [v0.13.5](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.5). ([#3661](https://github.com/kubernetes-sigs/external-dns/pull/3661)) [@GMartinez-Sisti](https://github.com/GMartinez-Sisti)
|
||||
- Adding missing gateway-httproute cluster role permission. ([#3541](https://github.com/kubernetes-sigs/external-dns/pull/3541)) [@nicon89](https://github.com/nicon89)
|
||||
|
||||
## [v1.12.2] - 2023-03-30
|
||||
|
||||
### 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.13.0
|
||||
appVersion: 0.13.5
|
||||
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:
|
||||
@ -21,8 +21,6 @@ maintainers:
|
||||
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: "Updated _ExternalDNS_ version to [v0.13.5](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.5)."
|
||||
- kind: changed
|
||||
description: "Changed to use registry.k8s.io instead of k8s.gcr.io."
|
||||
description: "Adding missing gateway-httproute cluster role permission."
|
||||
|
@ -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
charts/external-dns/ci/ci-values.yaml
Normal file
1
charts/external-dns/ci/ci-values.yaml
Normal file
@ -0,0 +1 @@
|
||||
provider: inmemory
|
@ -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 }}
|
||||
|
@ -43,8 +43,11 @@ shareProcessNamespace: false
|
||||
|
||||
podSecurityContext:
|
||||
fsGroup: 65534
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65534
|
||||
readOnlyRootFilesystem: true
|
||||
@ -151,6 +154,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.
|
||||
@ -168,11 +195,10 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
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,35 +208,14 @@ 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 {
|
||||
// Add missing records before the actual plan is applied.
|
||||
// This prevents the problems when the missing TXT record needs to be
|
||||
// created and deleted/upserted in the same batch.
|
||||
missingRecordsPlan := &plan.Plan{
|
||||
Policies: []plan.Policy{c.Policy},
|
||||
Missing: missingRecords,
|
||||
DomainFilter: endpoint.MatchAllDomainFilters{c.DomainFilter, c.Registry.GetDomainFilter()},
|
||||
PropertyComparator: c.Registry.PropertyValuesEqual,
|
||||
ManagedRecords: c.ManagedRecordTypes,
|
||||
}
|
||||
missingRecordsPlan = missingRecordsPlan.Calculate()
|
||||
if missingRecordsPlan.Changes.HasChanges() {
|
||||
err = c.Registry.ApplyChanges(ctx, missingRecordsPlan.Changes)
|
||||
if err != nil {
|
||||
registryErrorsTotal.Inc()
|
||||
deprecatedRegistryErrors.Inc()
|
||||
return err
|
||||
}
|
||||
log.Info("All missing records are created")
|
||||
}
|
||||
}
|
||||
|
||||
plan := &plan.Plan{
|
||||
Policies: []plan.Policy{c.Policy},
|
||||
Current: records,
|
||||
@ -238,30 +243,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 +311,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)
|
||||
@ -282,51 +311,6 @@ func testControllerFiltersDomains(t *testing.T, configuredEndpoints []*endpoint.
|
||||
}
|
||||
}
|
||||
|
||||
type noopRegistryWithMissing struct {
|
||||
*registry.NoopRegistry
|
||||
missingRecords []*endpoint.Endpoint
|
||||
}
|
||||
|
||||
func (r *noopRegistryWithMissing) MissingRecords() []*endpoint.Endpoint {
|
||||
return r.missingRecords
|
||||
}
|
||||
|
||||
func testControllerFiltersDomainsWithMissing(t *testing.T, configuredEndpoints []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, providerEndpoints, missingEndpoints []*endpoint.Endpoint, expectedChanges []*plan.Changes) {
|
||||
t.Helper()
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
|
||||
source := new(testutils.MockSource)
|
||||
source.On("Endpoints").Return(configuredEndpoints, nil)
|
||||
|
||||
// Fake some existing records in our DNS provider and validate some desired changes.
|
||||
provider := &filteredMockProvider{
|
||||
RecordsStore: providerEndpoints,
|
||||
}
|
||||
noop, err := registry.NewNoopRegistry(provider)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := &noopRegistryWithMissing{
|
||||
NoopRegistry: noop,
|
||||
missingRecords: missingEndpoints,
|
||||
}
|
||||
|
||||
ctrl := &Controller{
|
||||
Source: source,
|
||||
Registry: r,
|
||||
Policy: &plan.SyncPolicy{},
|
||||
DomainFilter: domainFilter,
|
||||
ManagedRecordTypes: cfg.ManagedDNSRecordTypes,
|
||||
}
|
||||
|
||||
assert.NoError(t, ctrl.RunOnce(context.Background()))
|
||||
assert.Equal(t, 1, provider.RecordsCallCount)
|
||||
require.Len(t, provider.ApplyChangesCalls, len(expectedChanges))
|
||||
for i, change := range expectedChanges {
|
||||
assert.Equal(t, *change, *provider.ApplyChangesCalls[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerSkipsEmptyChanges(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
@ -526,6 +510,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) {
|
||||
@ -575,56 +638,49 @@ func TestARecords(t *testing.T) {
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryARecords))
|
||||
}
|
||||
|
||||
// TestMissingRecordsApply validates that the missing records result in the dedicated plan apply.
|
||||
func TestMissingRecordsApply(t *testing.T) {
|
||||
testControllerFiltersDomainsWithMissing(
|
||||
func TestAAAARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
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.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
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"},
|
||||
},
|
||||
},
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "a-record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeTXT,
|
||||
Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{
|
||||
// Missing record had its own plan applied.
|
||||
{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "a-record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeTXT,
|
||||
Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceAAAARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryAAAARecords))
|
||||
}
|
||||
|
@ -15,10 +15,11 @@ Here is typical example of [CRD API type](https://github.com/kubernetes-sigs/ext
|
||||
type TTL int64
|
||||
type Targets []string
|
||||
type ProviderSpecificProperty struct {
|
||||
Name string
|
||||
Value string
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
type ProviderSpecific []ProviderSpecificProperty
|
||||
type Labels map[string]string
|
||||
|
||||
type Endpoint struct {
|
||||
// The hostname of the DNS record
|
||||
|
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"
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Quick Start
|
||||
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Go 1.19+](https://golang.org/dl/)
|
||||
- [Go 1.20+](https://golang.org/dl/)
|
||||
- [Go modules](https://github.com/golang/go/wiki/Modules)
|
||||
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
- [Docker](https://docs.docker.com/install/)
|
||||
|
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?
|
||||
|
||||
|
@ -1,124 +0,0 @@
|
||||
# Registry
|
||||
#### [Old name: storage]
|
||||
|
||||
Initial discussion - https://github.com/kubernetes-sigs/external-dns/issues/44
|
||||
|
||||
## Purpose
|
||||
|
||||
One should not be afraid to use external-dns, because it can delete or overwrite the records preexisting in the DNS provider.
|
||||
|
||||
**Why we need it?**
|
||||
|
||||
DNS provider (AWS Route53, Google DNS, etc.) stores dns records which are created via various means. Integration of External-DNS should be safe and should not delete or overwrite the records which it is not responsible for. Moreover, it should certainly be possible for multiple kubernetes clusters to share the same hosted zone within the dns provider, additionally multiple external-dns instances inside the same cluster should be able to co-exist without messing with the same set of records.
|
||||
|
||||
Registry provides a mechanism to ensure the safe management of DNS records
|
||||
|
||||
This proposal introduces multiple possible implementation with the details depending on the setup.
|
||||
|
||||
## Requirements and assumptions
|
||||
|
||||
1. Pre-existing records should not be modified by external-dns
|
||||
2. External-dns instance only creates/modifies/deletes records which are created by this instance
|
||||
3. It should be possible to transfer the ownership to another external-dns instance
|
||||
4. ~~Any integrated DNS provider should provide at least a single way to implement the registry~~ Noop registry can be used to disable ownership
|
||||
5. Lifetime of the records should not be limited to lifetime of external-dns
|
||||
6. External-dns should have its identifier for marking the managed records - **`owner-id`**
|
||||
|
||||
## Types of registry
|
||||
|
||||
The following presents two ways to implement the registry, and we are planning to implement both for compatibility purposes.
|
||||
|
||||
### TXT records
|
||||
|
||||
This implementation idea is borrowed from [Mate](https://github.com/linki/mate/)
|
||||
|
||||
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `owner-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `<record_type>-foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `owner-id`, then external-dns will simply ignore it.
|
||||
|
||||
#### Goods
|
||||
1. Easy to guarantee cross-cluster ownership safety
|
||||
2. Data lifetime is not limited to cluster or external-dns lifetime
|
||||
3. Supported by major DNS providers
|
||||
4. TXT record are created alongside other records in a batch request. Hence **eliminating possibility of having inconsistent ownership information and dns provider state**
|
||||
|
||||
#### Bads
|
||||
1. TXT record cannot co-exist with CNAME records (this can be solved by creating a TXT record with another domain name, e.g. `foo.org->foo.txt.org`)
|
||||
2. Introduces complexity to the logic
|
||||
3. Difficult to do the transfer of ownership
|
||||
4. Too easy to mess up with manual modifications
|
||||
|
||||
### ConfigMap
|
||||
|
||||
**This implementation cannot be considered 100% error free**, hence use with caution [see **Possible failure scenario** below]
|
||||
|
||||
Store the state in the configmap. ConfigMap is created and managed by each external-dns individually, i.e. external-dns with **`owner-id=external-dns-1`** will create and operate on `extern-dns-1-registry` ConfigMap. ConfigMap will store **all** the records present in the DNS provider as serialized JSON. For example:
|
||||
|
||||
```
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
creationTimestamp: 2016-03-09T19:14:38Z
|
||||
name: external-dns-1-storage
|
||||
namespace: same-as-external-dns-1
|
||||
data:
|
||||
records: "[{
|
||||
\"dnsname\": \"foo.org\",
|
||||
\"owner\": \"external-dns-1\",
|
||||
\"target\": \"loadbalancer1.com\",
|
||||
\"type\": \"A\"
|
||||
}, {
|
||||
\"dnsname\": \"bar.org\",
|
||||
\"owner\": \"external-dns-2\",
|
||||
\"target\": \"loadbalancer2.com\",
|
||||
\"type\": \"A\"
|
||||
}, {
|
||||
\"dnsname\": \"unmanaged.org\",
|
||||
\"owner\": \"\",
|
||||
\"target\": \"loadbalancer2.com\",
|
||||
\"type\": \"CNAME\"
|
||||
}]"
|
||||
|
||||
```
|
||||
|
||||
ConfigMap will be periodically resynced with the dns provider by fetching the dns records and comparing it with the data currently stored and hence rebuilding the ownership information.
|
||||
|
||||
#### Goods
|
||||
1. Not difficult to implement and easy to do the ownership transfer
|
||||
2. ConfigMap is a first class citizen in kubernetes world
|
||||
3. Does not create dependency/restriction on DNS provider
|
||||
4. Cannot be easily messed with by other parties
|
||||
|
||||
#### Bads
|
||||
1. ConfigMap might be out of sync with dns provider state
|
||||
2. LifeTime is obviously limited to the cluster lifetime
|
||||
3. Not supported in older kubernetes clusters
|
||||
4. Bloated ConfigMap - cannot be paginated and will grow very big on DNS provider with thousands of records
|
||||
|
||||
#### Failure scenario
|
||||
|
||||
It is possible that the configmap will go out of sync with the dns provider state. In the implementation flow external-dns will first modify records on dns provider side to subsequently update configmap. And if ExternalDNS will crash in-between two operation created records will be left unmanaged and not viable for update/deletion by External DNS.
|
||||
|
||||
## Component integration
|
||||
|
||||
Components:
|
||||
* Source - all endpoints ( collection of ingress, service[type=LoadBalancer] etc.)
|
||||
* [Plan](https://github.com/kubernetes-sigs/external-dns/issues/13) - object responsible for the create of change lists in external-dns
|
||||
* Provider - interface to access the DNS provider API
|
||||
|
||||
Registry will serve as wrapper around `Provider` providing additional information regarding endpoint ownership. Ownership will further taken into account by `Plan` to filter out records to include only records managed by current ExternalDNS instance (having same `owner-id` value)
|
||||
|
||||
A single loop iteration of external-dns operation:
|
||||
|
||||
1. Get all endpoints ( collection ingress, service[type=LoadBalancer] etc.) into collection of `endpoints`
|
||||
2. Get registry `Records()` (makes the call to DNSProvider and also build ownership information)
|
||||
3. Pass `Records` (including ownership information) and list of endpoints to `Plan` to do the calculation
|
||||
4. Call registry `ApplyChanges()` method (which subsequently calls DNS Provider Apply method to update records)
|
||||
5. If ConfigMap implementation of Registry is used, then ConfigMap needs to be updated separately
|
||||
|
||||
~~In case of configmap, Registry gets updated all the time via `Poll`. `Plan` does not call DNS provider directly. Good value of the `Poll` is to have simple rate limiting mechanism on DNS provider.~~
|
||||
|
||||
#### Notes:
|
||||
|
||||
1. DNS Provider should use batch operations
|
||||
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
### TXT Registry migration to a new format ###
|
||||
|
||||
In order to support more record types and be able to track ownership without TXT record name clash, a new TXT record is introduced.
|
||||
It contains record type it manages, e.g.:
|
||||
* A record foo.example.com will be tracked with classic foo.example.com TXT record
|
||||
* At the same time a new TXT record will be created a-foo.example.com
|
||||
|
||||
Prefix and suffix are extended with %{record_type} template where the user can control how prefixed/suffixed records should look like.
|
||||
|
||||
In order to maintain compatibility, both records will be maintained for some time, in order to have downgrade possibility.
|
||||
The controller will try to create the "new format" TXT records if they are not present to ease the migration from the versions < 0.12.0.
|
||||
|
||||
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.
|
41
docs/registry/dynamodb.md
Normal file
41
docs/registry/dynamodb.md
Normal file
@ -0,0 +1,41 @@
|
||||
# The DynamoDB registry
|
||||
|
||||
The DynamoDB registry stores DNS record metadata in an AWS DynamoDB table.
|
||||
|
||||
## The DynamoDB Table
|
||||
|
||||
By default, the DynamoDB registry stores data in the table named `external-dns`.
|
||||
A different table may be specified using the `--dynamodb-table` flag.
|
||||
A different region may be specified using the `--dynamodb-region` flag.
|
||||
|
||||
The table must have a partition (hash) key named `k` and string type.
|
||||
The table must not have a sort (range) key.
|
||||
|
||||
## IAM permissions
|
||||
|
||||
The ExternalDNS Role must be granted the following permissions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"DynamoDB:DescribeTable",
|
||||
"DynamoDB:PartiQLDelete",
|
||||
"DynamoDB:PartiQLInsert",
|
||||
"DynamoDB:PartiQLUpdate",
|
||||
"DynamoDB:Scan"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:dynamodb:*:*:table/external-dns"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The region and account ID may be specified explicitly specified instead of using wildcards.
|
||||
|
||||
## Caching
|
||||
|
||||
The DynamoDB registry can optionally cache DNS records read from the provider. This can mitigate
|
||||
rate limits imposed by the provider.
|
||||
|
||||
Caching is enabled by specifying a cache duration with the `--txt-cache-interval` flag.
|
17
docs/registry/registry.md
Normal file
17
docs/registry/registry.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Registries
|
||||
|
||||
A registry persists metadata pertaining to DNS records.
|
||||
|
||||
The most important metadata is the owning external-dns deployment.
|
||||
This is specified using the `--txt-owner-id` flag, specifying a value unique to the
|
||||
deployment of external-dns and which doesn't change for the lifetime of the deployment.
|
||||
Deployments in different clusters but sharing a DNS zone need to use different owner IDs.
|
||||
|
||||
The registry implementation is specified using the `--registry` flag.
|
||||
|
||||
## Supported registries
|
||||
|
||||
* [txt](txt.md) (default) - Stores metadata in TXT records in the same provider.
|
||||
* [dynamodb](dynamodb.md) - Stores metadata in an AWS DynamoDB table.
|
||||
* noop - Passes metadata directly to the provider. For most providers, this means the metadata is not persisted.
|
||||
* aws-sd - Stores metadata in AWS Service Discovery. Only usable with the `aws-sd` provider.
|
97
docs/registry/txt.md
Normal file
97
docs/registry/txt.md
Normal file
@ -0,0 +1,97 @@
|
||||
# The TXT registry
|
||||
|
||||
The TXT registry is the default registry.
|
||||
It stores DNS record metadata in TXT records, using the same provider.
|
||||
|
||||
## Prefixes and Suffixes
|
||||
|
||||
In order to avoid having the registry TXT records collide with
|
||||
TXT or CNAME records created from sources, you can configure a fixed prefix or suffix
|
||||
to be added to the first component of the domain of all registry TXT records.
|
||||
|
||||
The prefix or suffix may not be changed after initial deployment,
|
||||
lest the registry records be orphaned and the metadata be lost.
|
||||
|
||||
The prefix or suffix may contain the substring `%{record_type}`, which is replaced with
|
||||
the record type of the DNS record for which it is storing metadata.
|
||||
|
||||
The prefix is specified using the `--txt-prefix` flag and the suffix is specified using
|
||||
the `--txt-suffix` flag. The two flags are mutually exclusive.
|
||||
|
||||
## Wildcard Replacement
|
||||
|
||||
The `--txt-wildcard-replacement` flag specifies a string to use to replace the "*" in
|
||||
registry TXT records for wildcard domains. Without using this, registry TXT records for
|
||||
wildcard domains will have invalid domain syntax and be rejected by most providers.
|
||||
|
||||
## Encryption
|
||||
|
||||
Registry TXT records may contain information, such as the internal ingress name or namespace, considered sensitive, , which attackers could exploit to gather information about your infrastructure.
|
||||
By encrypting TXT records, you can protect this information from unauthorized access.
|
||||
|
||||
Encryption is enabled by using the `--txt-encrypt-enabled` flag. The 32-byte AES-256-GCM encryption
|
||||
key must be specified in URL-safe base64 form, using the `--txt-encrypt-aes-key` flag.
|
||||
|
||||
Note that the key used for encryption should be a secure key and properly managed to ensure the security of your TXT records.
|
||||
|
||||
### Generating the TXT Encryption 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 Encrypting/Decrypting TXT Records
|
||||
|
||||
In some cases you might need to edit registry TXT records. The following example Go code encrypts and decrypts such records.
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
The TXT registry can optionally cache DNS records read from the provider. This can mitigate
|
||||
rate limits imposed by the provider.
|
||||
|
||||
Caching is enabled by specifying a cache duration with the `--txt-cache-interval` flag.
|
@ -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:
|
||||
|
@ -50,7 +50,7 @@ The following fields are used:
|
||||
* `tenantId` (**required**) - run `az account show --query "tenantId"` or by selecting Azure Active Directory in the Azure Portal and checking the _Directory ID_ under Properties.
|
||||
* `subscriptionId` (**required**) - run `az account show --query "id"` or by selecting Subscriptions in the Azure Portal.
|
||||
* `resourceGroup` (**required**) is the Resource Group created in a previous step that contains the Azure DNS Zone.
|
||||
* `aadClientID` and `aaClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section.
|
||||
* `aadClientID` and `aadClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section.
|
||||
* `useManagedIdentityExtension` - this is set to `true` if you use either AKS Kubelet Identity or AAD Pod Identities methods documented in the next section.
|
||||
* `userAssignedIdentityID` - this contains the client id from the Managed identitty when using the AAD Pod Identities method documented in the next setion.
|
||||
|
||||
@ -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
|
||||
|
33
docs/tutorials/f5-virtualserver.md
Normal file
33
docs/tutorials/f5-virtualserver.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Configuring ExternalDNS to use the F5 Networks VirtualServer Source
|
||||
This tutorial describes how to configure ExternalDNS to use the F5 Networks VirtualServer Source. It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
The F5 Networks VirtualServer CRD is part of [this](https://github.com/F5Networks/k8s-bigip-ctlr) project. See more in-depth info regarding the VirtualServer CRD [here](https://github.com/F5Networks/k8s-bigip-ctlr/blob/master/docs/config_examples/customResource/CustomResource.md#virtualserver).
|
||||
|
||||
## Start with ExternalDNS with the F5 Networks VirtualServer source
|
||||
|
||||
1. Make sure that you have the `k8s-bigip-ctlr` installed in your cluster. The needed CRDs are bundled within the controller.
|
||||
|
||||
2. In your Helm `values.yaml` add:
|
||||
```
|
||||
sources:
|
||||
- ...
|
||||
- f5-virtualserver
|
||||
- ...
|
||||
```
|
||||
or add it in your `Deployment` if you aren't installing `external-dns` via Helm:
|
||||
```
|
||||
args:
|
||||
- --source=f5-virtualserver
|
||||
```
|
||||
|
||||
Note that, in case you're not installing via Helm, you'll need the following in the `ClusterRole` bound to the service account of `external-dns`:
|
||||
```
|
||||
- apiGroups:
|
||||
- cis.f5.com
|
||||
resources:
|
||||
- virtualservers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
```
|
@ -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 # comma separated gloo system namespace list. 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 # comma separated gloo system namespace list. 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: {}
|
||||
|
96
docs/tutorials/traefik-proxy.md
Normal file
96
docs/tutorials/traefik-proxy.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Configuring ExternalDNS to use the Traefik Proxy Source
|
||||
|
||||
This tutorial describes how to configure ExternalDNS to use the Traefik Proxy source.
|
||||
It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
## Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.3
|
||||
args:
|
||||
- --source=traefik-proxy
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
||||
## Manifest (for clusters with RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
- apiGroups: ["traefik.containo.us","traefik.io"]
|
||||
resources: ["ingressroutes", "ingressroutetcps", "ingressrouteudps"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.3
|
||||
args:
|
||||
- --source=traefik-proxy
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
@ -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.")
|
||||
}
|
||||
}
|
@ -24,41 +24,22 @@ import (
|
||||
// DomainFilterInterface defines the interface to select matching domains for a specific provider or runtime
|
||||
type DomainFilterInterface interface {
|
||||
Match(domain string) bool
|
||||
IsConfigured() bool
|
||||
}
|
||||
|
||||
type MatchAllDomainFilters []DomainFilterInterface
|
||||
|
||||
func (f MatchAllDomainFilters) Match(domain string) bool {
|
||||
if !f.IsConfigured() {
|
||||
return true
|
||||
}
|
||||
for _, filter := range f {
|
||||
if filter == nil {
|
||||
continue
|
||||
}
|
||||
if filter.IsConfigured() && !filter.Match(domain) {
|
||||
if !filter.Match(domain) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (f MatchAllDomainFilters) IsConfigured() bool {
|
||||
if f == nil {
|
||||
return false
|
||||
}
|
||||
for _, filter := range f {
|
||||
if filter == nil {
|
||||
continue
|
||||
}
|
||||
if filter.IsConfigured() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return len(f) > 0
|
||||
}
|
||||
|
||||
// DomainFilter holds a lists of valid domain names
|
||||
type DomainFilter struct {
|
||||
// Filters define what domains to match
|
||||
|
@ -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
|
||||
@ -158,13 +162,20 @@ type ProviderSpecificProperty struct {
|
||||
// ProviderSpecific holds configuration which is specific to individual DNS providers
|
||||
type ProviderSpecific []ProviderSpecificProperty
|
||||
|
||||
// EndpointKey is the type of a map key for separating endpoints or targets.
|
||||
type EndpointKey struct {
|
||||
DNSName string
|
||||
RecordType string
|
||||
SetIdentifier string
|
||||
}
|
||||
|
||||
// Endpoint is a high-level way of a connection between a service and an IP
|
||||
type Endpoint struct {
|
||||
// The hostname of the DNS record
|
||||
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"`
|
||||
@ -218,22 +229,52 @@ func (e *Endpoint) WithSetIdentifier(setIdentifier string) *Endpoint {
|
||||
// warrant its own field on the Endpoint object itself. It differs from Labels in the fact that it's
|
||||
// not persisted in the Registry but only kept in memory during a single record synchronization.
|
||||
func (e *Endpoint) WithProviderSpecific(key, value string) *Endpoint {
|
||||
if e.ProviderSpecific == nil {
|
||||
e.ProviderSpecific = ProviderSpecific{}
|
||||
}
|
||||
|
||||
e.ProviderSpecific = append(e.ProviderSpecific, ProviderSpecificProperty{Name: key, Value: value})
|
||||
e.SetProviderSpecificProperty(key, value)
|
||||
return e
|
||||
}
|
||||
|
||||
// GetProviderSpecificProperty returns a ProviderSpecificProperty if the property exists.
|
||||
func (e *Endpoint) GetProviderSpecificProperty(key string) (ProviderSpecificProperty, bool) {
|
||||
// GetProviderSpecificProperty returns the value of a ProviderSpecificProperty if the property exists.
|
||||
func (e *Endpoint) GetProviderSpecificProperty(key string) (string, bool) {
|
||||
for _, providerSpecific := range e.ProviderSpecific {
|
||||
if providerSpecific.Name == key {
|
||||
return providerSpecific, true
|
||||
return providerSpecific.Value, true
|
||||
}
|
||||
}
|
||||
return ProviderSpecificProperty{}, false
|
||||
return "", false
|
||||
}
|
||||
|
||||
// SetProviderSpecificProperty sets the value of a ProviderSpecificProperty.
|
||||
func (e *Endpoint) SetProviderSpecificProperty(key string, value string) {
|
||||
for i, providerSpecific := range e.ProviderSpecific {
|
||||
if providerSpecific.Name == key {
|
||||
e.ProviderSpecific[i] = ProviderSpecificProperty{
|
||||
Name: key,
|
||||
Value: value,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
e.ProviderSpecific = append(e.ProviderSpecific, ProviderSpecificProperty{Name: key, Value: value})
|
||||
}
|
||||
|
||||
// DeleteProviderSpecificProperty deletes any ProviderSpecificProperty of the specified name.
|
||||
func (e *Endpoint) DeleteProviderSpecificProperty(key string) {
|
||||
for i, providerSpecific := range e.ProviderSpecific {
|
||||
if providerSpecific.Name == key {
|
||||
e.ProviderSpecific = append(e.ProviderSpecific[:i], e.ProviderSpecific[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the EndpointKey of the Endpoint.
|
||||
func (e *Endpoint) Key() EndpointKey {
|
||||
return EndpointKey{
|
||||
DNSName: e.DNSName,
|
||||
RecordType: e.RecordType,
|
||||
SetIdentifier: e.SetIdentifier,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Endpoint) String() string {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import (
|
||||
// TargetFilterInterface defines the interface to select matching targets for a specific provider or runtime
|
||||
type TargetFilterInterface interface {
|
||||
Match(target string) bool
|
||||
IsConfigured() bool
|
||||
}
|
||||
|
||||
// TargetNetFilter holds a lists of valid target names
|
||||
@ -61,11 +60,6 @@ func NewTargetNetFilterWithExclusions(targetFilterNets []string, excludeNets []s
|
||||
return TargetNetFilter{FilterNets: prepareTargetFilters(targetFilterNets), excludeNets: prepareTargetFilters(excludeNets)}
|
||||
}
|
||||
|
||||
// NewTargetNetFilter returns a new TargetNetFilter given a comma separated list of targets
|
||||
func NewTargetNetFilter(targetFilterNets []string) TargetNetFilter {
|
||||
return TargetNetFilter{FilterNets: prepareTargetFilters(targetFilterNets)}
|
||||
}
|
||||
|
||||
// Match checks whether a target can be found in the TargetNetFilter.
|
||||
func (tf TargetNetFilter) Match(target string) bool {
|
||||
return matchTargetNetFilter(tf.FilterNets, target, true) && !matchTargetNetFilter(tf.excludeNets, target, false)
|
||||
@ -89,11 +83,3 @@ func matchTargetNetFilter(filters []*net.IPNet, target string, emptyval bool) bo
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConfigured returns true if TargetFilter is configured, false otherwise
|
||||
func (tf TargetNetFilter) IsConfigured() bool {
|
||||
if len(tf.FilterNets) == 1 {
|
||||
return tf.FilterNets[0].Network() != ""
|
||||
}
|
||||
return len(tf.FilterNets) > 0
|
||||
}
|
||||
|
@ -68,19 +68,6 @@ var targetFilterTests = []targetFilterTest{
|
||||
},
|
||||
}
|
||||
|
||||
func TestTargetFilterMatch(t *testing.T) {
|
||||
for i, tt := range targetFilterTests {
|
||||
if len(tt.exclusions) > 0 {
|
||||
t.Logf("NewTargetFilter() doesn't support exclusions - skipping test %+v", tt)
|
||||
continue
|
||||
}
|
||||
targetFilter := NewTargetNetFilter(tt.targetFilter)
|
||||
for _, target := range tt.targets {
|
||||
assert.Equal(t, tt.expected, targetFilter.Match(target), "should not fail: %v in test-case #%v", target, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetFilterWithExclusions(t *testing.T) {
|
||||
for i, tt := range targetFilterTests {
|
||||
if len(tt.exclusions) == 0 {
|
||||
@ -107,47 +94,3 @@ func TestMatchTargetFilterReturnsProperEmptyVal(t *testing.T) {
|
||||
assert.Equal(t, true, matchFilter(emptyFilters, "sometarget.com", true))
|
||||
assert.Equal(t, false, matchFilter(emptyFilters, "sometarget.com", false))
|
||||
}
|
||||
|
||||
func TestTargetFilterIsConfigured(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
filters []string
|
||||
exclude []string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
[]string{""},
|
||||
[]string{""},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{" "},
|
||||
[]string{" "},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"", ""},
|
||||
[]string{""},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"10/8"},
|
||||
[]string{" "},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"10.0.0.0/8"},
|
||||
[]string{" "},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{" 10.0.0.0/8 "},
|
||||
[]string{" ignored "},
|
||||
true,
|
||||
},
|
||||
} {
|
||||
t.Run("test IsConfigured", func(t *testing.T) {
|
||||
tf := NewTargetNetFilterWithExclusions(tt.filters, tt.exclude)
|
||||
assert.Equal(t, tt.expected, tf.IsConfigured())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
231
go.mod
231
go.mod
@ -1,81 +1,83 @@
|
||||
module sigs.k8s.io/external-dns
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3
|
||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.28
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.21
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.29
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0
|
||||
github.com/IBM/go-sdk-core/v5 v5.8.0
|
||||
github.com/IBM/networking-go-sdk v0.36.0
|
||||
github.com/StackExchange/dnscontrol/v3 v3.27.1
|
||||
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.13.0
|
||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.1.0
|
||||
github.com/IBM/go-sdk-core/v5 v5.13.4
|
||||
github.com/IBM/networking-go-sdk v0.42.0
|
||||
github.com/StackExchange/dnscontrol/v3 v3.31.6
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
|
||||
github.com/alecthomas/kingpin v2.2.5+incompatible
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.4
|
||||
github.com/ans-group/sdk-go v1.10.4
|
||||
github.com/aws/aws-sdk-go v1.44.136
|
||||
github.com/bodgit/tsig v1.2.0
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.380
|
||||
github.com/ans-group/sdk-go v1.16.5
|
||||
github.com/aws/aws-sdk-go v1.44.285
|
||||
github.com/bodgit/tsig v1.2.2
|
||||
github.com/civo/civogo v0.3.14
|
||||
github.com/cloudflare/cloudflare-go v0.58.1
|
||||
github.com/cloudflare/cloudflare-go v0.69.0
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/datawire/ambassador v1.6.0
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
|
||||
github.com/digitalocean/godo v1.97.0
|
||||
github.com/dnsimple/dnsimple-go v1.0.1
|
||||
github.com/exoscale/egoscale v0.97.0
|
||||
github.com/digitalocean/godo v1.99.0
|
||||
github.com/dnsimple/dnsimple-go v1.2.0
|
||||
github.com/exoscale/egoscale v1.19.0
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
||||
github.com/go-gandi/go-gandi v0.6.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/gophercloud/gophercloud v0.25.0
|
||||
github.com/gophercloud/gophercloud v1.4.0
|
||||
github.com/hooklift/gowsdl v0.5.0
|
||||
github.com/infobloxopen/infoblox-go-client/v2 v2.1.2-0.20220407114022-6f4c71443168
|
||||
github.com/infobloxopen/infoblox-go-client/v2 v2.3.0
|
||||
github.com/linki/instrumented_http v0.3.0
|
||||
github.com/linode/linodego v1.9.1
|
||||
github.com/maxatome/go-testdeep v1.12.0
|
||||
github.com/miekg/dns v1.1.51
|
||||
github.com/linode/linodego v1.17.0
|
||||
github.com/maxatome/go-testdeep v1.13.0
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/nesv/go-dynect v0.6.0
|
||||
github.com/nic-at/rc0go v1.1.1
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae
|
||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible
|
||||
github.com/ovh/go-ovh v1.1.0
|
||||
github.com/openshift/api v0.0.0-20230607130528-611114dca681
|
||||
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
|
||||
github.com/oracle/oci-go-sdk/v65 v65.41.0
|
||||
github.com/ovh/go-ovh v1.4.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pluralsh/gqlclient v1.1.6
|
||||
github.com/projectcontour/contour v1.23.2
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.599
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.344
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.599
|
||||
github.com/transip/gotransip/v6 v6.19.0
|
||||
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556
|
||||
github.com/pluralsh/gqlclient v1.3.17
|
||||
github.com/projectcontour/contour v1.25.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.684
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.684
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.684
|
||||
github.com/transip/gotransip/v6 v6.20.0
|
||||
github.com/ultradns/ultradns-sdk-go v1.3.7
|
||||
github.com/vinyldns/go-vinyldns v0.9.16
|
||||
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.9
|
||||
go.etcd.io/etcd/client/v3 v3.5.9
|
||||
go.uber.org/ratelimit v0.2.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/oauth2 v0.5.0
|
||||
golang.org/x/sync v0.1.0
|
||||
google.golang.org/api v0.110.0
|
||||
gopkg.in/ns1/ns1-go.v2 v2.7.4
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/oauth2 v0.9.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/api v0.128.0
|
||||
gopkg.in/ns1/ns1-go.v2 v2.7.6
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
istio.io/api v0.0.0-20210128181506-0c4b8e54850f
|
||||
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02
|
||||
k8s.io/api v0.26.0
|
||||
k8s.io/apimachinery v0.26.0
|
||||
k8s.io/client-go v0.26.0
|
||||
sigs.k8s.io/gateway-api v0.6.0
|
||||
istio.io/api v0.0.0-20230524015941-fa6c5f7916bf
|
||||
istio.io/client-go v1.18.0
|
||||
k8s.io/api v0.27.3
|
||||
k8s.io/apimachinery v0.27.3
|
||||
k8s.io/client-go v0.27.3
|
||||
sigs.k8s.io/gateway-api v0.7.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.18.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.3 // indirect
|
||||
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
@ -83,66 +85,68 @@ require (
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/Masterminds/semver v1.4.2 // indirect
|
||||
github.com/Yamashou/gqlgenc v0.11.0 // indirect
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||
github.com/alecthomas/colour v0.1.0 // indirect
|
||||
github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/ans-group/go-durationstring v1.2.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/errors v0.19.8 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/strfmt v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
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/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.5 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.13.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.7.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
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.3 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/jinzhu/copier v0.3.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.3 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@ -154,51 +158,56 @@ require (
|
||||
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/peterhellberg/link v1.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.43.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
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/afero v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.15.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // 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.mongodb.org/mongo-driver v1.5.1 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
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/term v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/term v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/grpc v1.55.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.6 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/resty.v1 v1.12.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
|
||||
moul.io/http2curl v1.0.0 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.12.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.14.6 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace k8s.io/klog/v2 => github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user