mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
git: merge upstream master
Signed-off-by: Enrique Gonzalez <goga.enrique@gmail.com>
This commit is contained in:
commit
76817b350f
2
.github/ISSUE_TEMPLATE/-support-request.md
vendored
2
.github/ISSUE_TEMPLATE/-support-request.md
vendored
@ -2,7 +2,7 @@
|
||||
name: "❓Support Request"
|
||||
about: Support request or question relating to external-dns
|
||||
title: ''
|
||||
labels: triage/support
|
||||
labels: kind/support
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
17
.github/pull_request_template.md
vendored
17
.github/pull_request_template.md
vendored
@ -1,3 +1,16 @@
|
||||
## Checklist
|
||||
<!--
|
||||
Please read https://github.com/kubernetes-sigs/external-dns#contributing before submitting
|
||||
your pull request. Please fill in each section below to help us better prioritize your pull request. Thanks!
|
||||
-->
|
||||
|
||||
- [ ] Update changelog in CHANGELOG.md, use section "Unreleased".
|
||||
**Description**
|
||||
|
||||
<!-- Please provide a summary of the change here. -->
|
||||
|
||||
<!-- Please link to all GitHub issue that this pull request implements(i.e. Fixes #123) -->
|
||||
Fixes #ISSUE
|
||||
|
||||
**Checklist**
|
||||
|
||||
- [ ] Unit tests updated
|
||||
- [ ] End user documentation updated
|
||||
|
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.14
|
||||
go-version: ^1.15
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@ -30,15 +30,22 @@ jobs:
|
||||
dep ensure
|
||||
fi
|
||||
|
||||
- name: Install additional CI for nektos/act
|
||||
run: |
|
||||
apt update
|
||||
apt install -y make gcc libc-dev git
|
||||
if: github.actor == 'nektos/act'
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.26.0
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
|
||||
make lint
|
||||
|
||||
- name: Coverage
|
||||
uses: shogo82148/actions-goveralls@v1
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
|
||||
- name: Send coverage
|
||||
uses: shogo82148/actions-goveralls@v1
|
||||
with:
|
||||
path-to-profile: profile.cov
|
||||
if: github.actor != 'nektos/act'
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -46,3 +46,5 @@ external-dns
|
||||
|
||||
# vendor dir
|
||||
vendor/
|
||||
|
||||
profile.cov
|
||||
|
@ -2,7 +2,7 @@ linters-settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: false
|
||||
goimports:
|
||||
local-prefixes: github.com/kubernetes-sigs/external-dns
|
||||
local-prefixes: sigs.k8s.io/external-dns
|
||||
golint:
|
||||
min-confidence: 0.9
|
||||
maligned:
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,5 +1,21 @@
|
||||
## Unreleased
|
||||
|
||||
- Add quick start section to contributing docs (#1766) @seanmalloy
|
||||
- Enhance pull request template @seanmalloy
|
||||
- Improve errors context for AWS provider
|
||||
- Scaleway Provider (#1643) @Sh4d1
|
||||
- Enable azure_private_dns to work with non "AzurePublicCloud" clouds (#1578) @daddonpa
|
||||
- Fix typos in documentation @ddymko
|
||||
- Add Cloudflare documentation on use of `--zone-id-filter` (#1751) @loozhengyuan
|
||||
- Fix: alibaba cloud keeping create record (#1682) @LXM
|
||||
- Update all container registry references to use k8s.gcr.io @seanmalloy
|
||||
- Provide available prometheus metrics in documentation @vinny-sabatini
|
||||
- Fix index out of range when hostname has no dots (#1756) @chemasan
|
||||
- Fixes test coverage with coveralls (#1755) @jgrumboe
|
||||
- Add tutorial for GKE with workload identity (#1765) @ddgenome
|
||||
- Fix NodePort with externaltrafficpolicy targets duplication @codearky
|
||||
- Update contributing section in README (#1760) @seanmalloy
|
||||
- Option to cache AWS zones list @bpineau
|
||||
|
||||
## v0.7.3 - 2020-08-05
|
||||
|
||||
|
@ -1,22 +1,22 @@
|
||||
# Contributing guidelines
|
||||
# Contributing Guidelines
|
||||
|
||||
## How to become a contributor and submit your own code
|
||||
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:
|
||||
|
||||
### Contributor License Agreements
|
||||
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
|
||||
|
||||
We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles.
|
||||
## Getting Started
|
||||
|
||||
Please fill out either the individual or corporate Contributor License Agreement (CLA).
|
||||
We have full documentation on how to get started contributing here:
|
||||
|
||||
* If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an individual CLA.
|
||||
* If you work for a company that wants to allow you to contribute your work, then you'll need to sign a corporate CLA.
|
||||
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
|
||||
- [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing)
|
||||
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers
|
||||
|
||||
To sign and submit a CLA, see the [CLA doc](https://git.k8s.io/community/CLA.md).
|
||||
## Mentorship
|
||||
|
||||
### Contributing A Patch
|
||||
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
|
||||
|
||||
1. Submit an issue describing your proposed change to the repo in question.
|
||||
1. The [repo owners](OWNERS) will respond to your issue promptly.
|
||||
1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above).
|
||||
1. Fork the desired repo, develop and test your code changes.
|
||||
1. Submit a pull request.
|
||||
## Contact Information
|
||||
|
||||
- [Slack channel](https://kubernetes.slack.com/messages/external-dns)
|
||||
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-network)
|
||||
|
15
Dockerfile
15
Dockerfile
@ -13,22 +13,19 @@
|
||||
# limitations under the License.
|
||||
|
||||
# builder image
|
||||
FROM golang:1.14 as builder
|
||||
ARG ARCH
|
||||
FROM golang:1.15 as builder
|
||||
ARG ARCH
|
||||
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
|
||||
COPY . .
|
||||
RUN go mod vendor && \
|
||||
make test && \
|
||||
make build
|
||||
RUN make test && make build.$ARCH
|
||||
|
||||
# final image
|
||||
FROM alpine:3.12
|
||||
LABEL maintainer="Team Teapot @ Zalando SE <team-teapot@zalando.de>"
|
||||
|
||||
RUN apk add --update --no-cache ca-certificates && \
|
||||
update-ca-certificates
|
||||
FROM $ARCH/alpine:3.12
|
||||
|
||||
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
|
||||
|
||||
# Run as UID for nobody since k8s pod securityContext runAsNonRoot can't resolve the user ID:
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.14 as builder
|
||||
FROM golang:1.15 as builder
|
||||
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
|
||||
|
58
Makefile
58
Makefile
@ -27,18 +27,36 @@ cover:
|
||||
cover-html: cover
|
||||
go tool cover -html cover.out
|
||||
|
||||
.PHONY: go-lint
|
||||
|
||||
# Run the golangci-lint tool
|
||||
go-lint:
|
||||
golangci-lint run --timeout=15m ./...
|
||||
|
||||
.PHONY: licensecheck
|
||||
|
||||
# Run the licensecheck script to check for license headers
|
||||
licensecheck:
|
||||
@echo ">> checking license header"
|
||||
@licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \
|
||||
awk 'NR<=5' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
|
||||
done); \
|
||||
if [ -n "$${licRes}" ]; then \
|
||||
echo "license header checking failed:"; echo "$${licRes}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: lint
|
||||
|
||||
# Run all the linters
|
||||
lint:
|
||||
golangci-lint run --timeout=15m ./...
|
||||
lint: licensecheck go-lint
|
||||
|
||||
|
||||
# The verify target runs tasks similar to the CI tasks, but without code coverage
|
||||
.PHONY: verify test
|
||||
|
||||
test:
|
||||
go test -race ./...
|
||||
go test -race -coverprofile=profile.cov ./...
|
||||
|
||||
# The build targets allow to build the binary and docker image
|
||||
.PHONY: build build.docker build.mini
|
||||
@ -50,17 +68,45 @@ 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
|
||||
|
||||
|
||||
build: build/$(BINARY)
|
||||
|
||||
build/$(BINARY): $(SOURCES)
|
||||
CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.push/multiarch:
|
||||
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.12 ;\
|
||||
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
|
||||
docker push $${image} ;\
|
||||
arch_specific_tags+=( "--amend $${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)" \
|
||||
|
||||
build.push: build.docker
|
||||
docker push "$(IMAGE):$(VERSION)"
|
||||
|
||||
build.arm64v8:
|
||||
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:
|
||||
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)" .
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="amd64" .
|
||||
|
||||
build.mini:
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)-mini" --build-arg VERSION="$(VERSION)" -f Dockerfile.mini .
|
||||
@ -72,7 +118,7 @@ clean:
|
||||
.PHONY: release.staging
|
||||
|
||||
release.staging:
|
||||
IMAGE=$(IMAGE_STAGING) $(MAKE) build.docker build.push
|
||||
IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch
|
||||
|
||||
release.prod:
|
||||
$(MAKE) build.docker build.push
|
||||
$(MAKE) build.push/multiarch
|
||||
|
6
OWNERS
6
OWNERS
@ -2,7 +2,9 @@
|
||||
# https://github.com/kubernetes/community/blob/HEAD/contributors/guide/owners.md
|
||||
|
||||
approvers:
|
||||
- hjacobs
|
||||
- raffo
|
||||
- linki
|
||||
- njuettner
|
||||
|
||||
emeritus_approvers:
|
||||
- hjacobs
|
||||
- linki
|
||||
|
68
README.md
68
README.md
@ -46,6 +46,7 @@ ExternalDNS' current release is `v0.7`. This version allows you to keep selected
|
||||
* [TransIP](https://www.transip.eu/domain-name/)
|
||||
* [VinylDNS](https://www.vinyldns.io)
|
||||
* [OVH](https://www.ovh.com)
|
||||
* [Scaleway](https://www.scaleway.com)
|
||||
|
||||
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.
|
||||
|
||||
@ -71,7 +72,7 @@ We define the following stability levels for providers:
|
||||
|
||||
The following table clarifies the current status of the providers according to the aforementioned stability levels:
|
||||
|
||||
| Provider | Status | Maintainers |
|
||||
| Provider | Status | Maintainers |
|
||||
| -------- | ------ | ----------- |
|
||||
| Google Cloud DNS | Stable | |
|
||||
| AWS Route 53 | Stable | |
|
||||
@ -97,6 +98,7 @@ The following table clarifies the current status of the providers according to t
|
||||
| RancherDNS | Alpha | |
|
||||
| Akamai FastDNS | Alpha | |
|
||||
| OVH | Alpha | |
|
||||
| Scaleway DNS | Alpha | @Sh4d1 |
|
||||
| Vultr | Alpha | |
|
||||
| UltraDNS | Alpha | |
|
||||
|
||||
@ -138,6 +140,7 @@ The following tutorials are provided:
|
||||
* [Linode](docs/tutorials/linode.md)
|
||||
* [Nginx Ingress Controller](docs/tutorials/nginx-ingress.md)
|
||||
* [NS1](docs/tutorials/ns1.md)
|
||||
* [NS Record Creation with CRD Source](docs/tutorials/ns-record.md)
|
||||
* [OpenStack Designate](docs/tutorials/designate.md)
|
||||
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
|
||||
* [PowerDNS](docs/tutorials/pdns.md)
|
||||
@ -147,39 +150,17 @@ The following tutorials are provided:
|
||||
* [TransIP](docs/tutorials/transip.md)
|
||||
* [VinylDNS](docs/tutorials/vinyldns.md)
|
||||
* [OVH](docs/tutorials/ovh.md)
|
||||
* [Scaleway](docs/tutorials/scaleway.md)
|
||||
* [Vultr](docs/tutorials/vultr.md)
|
||||
* [UltraDNS](docs/tutorials/ultradns.md)
|
||||
|
||||
### Running Locally
|
||||
|
||||
#### Technical Requirements
|
||||
|
||||
Make sure you have the following prerequisites:
|
||||
* A local Go 1.11+ development environment.
|
||||
* Access to a Google/AWS account with the DNS API enabled.
|
||||
* Access to a Kubernetes cluster that supports exposing Services, e.g. GKE.
|
||||
See the [contributor guide](docs/contributing/getting-started.md) for details on compiling
|
||||
from source.
|
||||
|
||||
#### Setup Steps
|
||||
|
||||
First, get ExternalDNS:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/kubernetes-sigs/external-dns.git && cd external-dns
|
||||
```
|
||||
|
||||
**This project uses [Go modules](https://github.com/golang/go/wiki/Modules) as
|
||||
introduced in Go 1.11 therefore you need Go >=1.11 installed in order to build.**
|
||||
If using Go 1.11 you also need to [activate Module
|
||||
support](https://github.com/golang/go/wiki/Modules#installing-and-activating-module-support).
|
||||
|
||||
Assuming Go has been setup with module support it can be built simply by running:
|
||||
|
||||
```console
|
||||
$ make
|
||||
```
|
||||
|
||||
This will create external-dns in the build directory directly from the default branch.
|
||||
|
||||
Next, run an application and expose it via a Kubernetes Service:
|
||||
|
||||
```console
|
||||
@ -236,6 +217,8 @@ The [tutorials](docs/tutorials) section contains examples, including Ingress res
|
||||
|
||||
If using a txt registry and attempting to use a CNAME the `--txt-prefix` must be set to avoid conflicts. Changing `--txt-prefix` will result in lost ownership over previously created records.
|
||||
|
||||
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.
|
||||
@ -265,7 +248,7 @@ Here's a rough outline on what is to come (subject to change):
|
||||
- [x] Support for DigitalOcean
|
||||
- [x] Multiple DNS names per Service
|
||||
|
||||
### v0.5 - _current version_
|
||||
### v0.5
|
||||
|
||||
- [x] Support for creating DNS records to multiple targets (for Google and AWS)
|
||||
- [x] Support for OpenStack Designate
|
||||
@ -300,24 +283,23 @@ Have a look at [the milestones](https://github.com/kubernetes-sigs/external-dns/
|
||||
|
||||
## Contributing
|
||||
|
||||
We encourage you to get involved with ExternalDNS, as users, contributors or as new maintainers that can take over some parts like different providers and help with code reviews.
|
||||
Are you interested in contributing to external-dns? We, the maintainers and community, would love your
|
||||
suggestions, contributions, and help! Also, the maintainers can be contacted at any time to learn more
|
||||
about how to get involved.
|
||||
|
||||
Providers which currently need maintainers:
|
||||
We also encourage ALL active community participants to act as if they are maintainers, even if you don't have
|
||||
"official" write permissions. This is a community effort, we are here to serve the Kubernetes community. If you
|
||||
have an active interest and you want to get involved, you have real power! Don't assume that the only people who
|
||||
can get things done around here are the "maintainers". We also would love to add more "official" maintainers, so
|
||||
show us what you can do!
|
||||
|
||||
* Azure
|
||||
* Cloudflare
|
||||
* Digital Ocean
|
||||
* Google Cloud Platform
|
||||
|
||||
Any provider should have at least one maintainer. It would be nice if you run it in production, but it is not required.
|
||||
You should check changes and make sure your provider is working correctly.
|
||||
|
||||
It would be also great to have an automated end-to-end test for different cloud providers, so help from Kubernetes maintainers and their idea on how this can be done would be valuable.
|
||||
The external-dns project is currently in need of maintainers for specific DNS providers. Ideally each provider
|
||||
would have at least two maintainers. It would be nice if the maintainers run the provider in production, but it
|
||||
is not strictly required. Provider listed [here](https://github.com/kubernetes-sigs/external-dns#status-of-providers)
|
||||
that do not have a maintainer listed are in need of assistance.
|
||||
|
||||
Read the [contributing guidelines](CONTRIBUTING.md) and have a look at [the contributing docs](docs/contributing/getting-started.md) to learn about building the project, the project structure, and the purpose of each package.
|
||||
|
||||
If you are interested please reach out to us on the [Kubernetes slack](http://slack.k8s.io) in the #external-dns channel.
|
||||
|
||||
For an overview on how to write new Sources and Providers check out [Sources and Providers](docs/contributing/sources-and-providers.md).
|
||||
|
||||
## Heritage
|
||||
@ -329,8 +311,6 @@ ExternalDNS is an effort to unify the following similar projects in order to bri
|
||||
* Molecule Software's [route53-kubernetes](https://github.com/wearemolecule/route53-kubernetes)
|
||||
|
||||
### User Demo How-To Blogs and Examples
|
||||
|
||||
* A full demo on GKE Kubernetes. See [How-to Kubernetes with DNS management (ssl-manager pre-req)](https://medium.com/@jpantjsoha/how-to-kubernetes-with-dns-management-for-gitops-31239ea75d8d)
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
||||
* Run external-dns on GKE with workload identity. See [Kubernetes, ingress-nginx, cert-manager & external-dns](https://blog.atomist.com/kubernetes-ingress-nginx-cert-manager-external-dns/)
|
||||
|
@ -1,9 +1,9 @@
|
||||
# See https://cloud.google.com/cloud-build/docs/build-config
|
||||
timeout: 1200s
|
||||
timeout: 3000s
|
||||
options:
|
||||
substitution_option: ALLOW_LOOSE
|
||||
steps:
|
||||
- name: "gcr.io/k8s-testimages/gcb-docker-gcloud:v20190906-745fed4"
|
||||
- name: "gcr.io/k8s-testimages/gcb-docker-gcloud:v20200824-5d057db"
|
||||
entrypoint: make
|
||||
env:
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
@ -103,7 +103,7 @@ func init() {
|
||||
// * Ask the DNS provider for current list of endpoints.
|
||||
// * Ask the Source for the desired list of endpoints.
|
||||
// * Take both lists and calculate a Plan to move current towards desired state.
|
||||
// * Tell the DNS provider to apply the changes calucated by the Plan.
|
||||
// * Tell the DNS provider to apply the changes calculated by the Plan.
|
||||
type Controller struct {
|
||||
Source source.Source
|
||||
Registry registry.Registry
|
||||
|
@ -45,7 +45,7 @@ func (p *mockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error
|
||||
return p.RecordsStore, nil
|
||||
}
|
||||
|
||||
// ApplyChanges validates that the passed in changes satisfy the assumtions.
|
||||
// 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")
|
||||
|
@ -1,10 +1,33 @@
|
||||
# Project structure
|
||||
# Quick Start
|
||||
|
||||
### Building
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Go 1.15+](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/)
|
||||
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl)
|
||||
|
||||
You can build ExternalDNS for your platform with `make build`, you may have to install the necessary dependencies with `make dep`. The binary will land at `build/external-dns`.
|
||||
Compile and run locally against a remote k8s cluster.
|
||||
```
|
||||
git clone https://github.com/kubernetes-sigs/external-dns.git && cd external-dns
|
||||
make build
|
||||
# login to remote k8s cluster
|
||||
./build/external-dns --source=service --provider=inmemory --once
|
||||
```
|
||||
|
||||
### Design
|
||||
Run linting, unit tests, and coverage report.
|
||||
```
|
||||
make lint
|
||||
make test
|
||||
make cover-html
|
||||
```
|
||||
|
||||
Build container image.
|
||||
```
|
||||
make build.docker
|
||||
```
|
||||
|
||||
# Design
|
||||
|
||||
ExternalDNS's sources of DNS records live in package [source](../../source). They implement the `Source` interface that has a single method `Endpoints` which returns the represented source's objects converted to `Endpoints`. Endpoints are just a tuple of DNS name and target where target can be an IP or another hostname.
|
||||
|
||||
@ -20,8 +43,18 @@ The orchestration between the different components is controlled by the [control
|
||||
|
||||
You can pick which `Source` and `Provider` to use at runtime via the `--source` and `--provider` flags, respectively.
|
||||
|
||||
### Adding a DNS provider
|
||||
# Adding a DNS Provider
|
||||
|
||||
A typical way to start on, e.g. a CoreDNS provider, would be to add a `coredns.go` to the providers package and implement the interface methods. Then you would have to register your provider under a name in `main.go`, e.g. `coredns`, and would be able to trigger it's functions via setting `--provider=coredns`.
|
||||
|
||||
Note, how your provider doesn't need to know anything about where the DNS records come from, nor does it have to figure out the difference between the current and the desired state, it merely executes the actions calculated by the plan.
|
||||
|
||||
# Running Github Actions locally
|
||||
|
||||
You can also extend the CI workflow which is currently implemented as Github Action within the [workflow](../../.github/workflow) folder.
|
||||
In order to test your changes before commiting you can leverage [act](https://github.com/nektos/act) to run the Github Action locally.
|
||||
|
||||
Follow the installation instructions in the nektos/act [README.md](https://github.com/nektos/act/blob/master/README.md).
|
||||
Afterwards just run `act` within the root folder of the project.
|
||||
|
||||
For further usage of `act` refer to its documentation.
|
||||
|
40
docs/faq.md
40
docs/faq.md
@ -189,6 +189,16 @@ In case of an increased error count, you could correlate them with the `http_req
|
||||
|
||||
You can use the host label in the metric to figure out if the request was against the Kubernetes API server (Source errors) or the DNS provider API (Registry/Provider errors).
|
||||
|
||||
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 |
|
||||
|
||||
### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects?
|
||||
|
||||
Have a look at https://github.com/linki/mate/blob/v0.6.2/examples/google/README.md#permissions
|
||||
@ -204,10 +214,11 @@ $ docker run \
|
||||
-e EXTERNAL_DNS_SOURCE=$'service\ningress' \
|
||||
-e EXTERNAL_DNS_PROVIDER=google \
|
||||
-e EXTERNAL_DNS_DOMAIN_FILTER=$'foo.com\nbar.com' \
|
||||
registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
time="2017-08-08T14:10:26Z" level=info msg="config: &{APIServerURL: KubeConfig: Sources:[service ingress] Namespace: ...
|
||||
```
|
||||
|
||||
|
||||
Locally:
|
||||
|
||||
```console
|
||||
@ -261,6 +272,19 @@ an instance of a ingress controller. Let's assume you have two ingress controlle
|
||||
then you can start two ExternalDNS providers one with `--annotation-filter=kubernetes.io/ingress.class=nginx-internal`
|
||||
and one with `--annotation-filter=kubernetes.io/ingress.class=nginx-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`.
|
||||
|
||||
### 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?
|
||||
|
||||
If your Nodes have both public and private IP addresses, you might want to write DNS records with one or the other.
|
||||
For example, you may want to write a DNS record in a private zone that resolves to your Nodes' private IPs so that traffic never leaves your private network.
|
||||
|
||||
To accomplish this, set this annotation on your service: `external-dns.alpha.kubernetes.io/access=private`
|
||||
Conversely, to force the public IP: `external-dns.alpha.kubernetes.io/access=public`
|
||||
|
||||
If this annotation is not set, and the node has both public and private IP addresses, then the public IP will be used by default.
|
||||
|
||||
### Can external-dns manage(add/remove) records in a hosted zone which is setup in different AWS account?
|
||||
|
||||
Yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-sigs/external-dns/pull/524#issue-181256561
|
||||
@ -272,17 +296,23 @@ Separate them by `,`.
|
||||
|
||||
### Are there official Docker images provided?
|
||||
|
||||
When we tag a new release, we push a Docker image on Zalando's public Docker registry with the following name:
|
||||
When we tag a new release, we push a container image to the Kubernetes projects official container registry with the following name:
|
||||
|
||||
```
|
||||
registry.opensource.zalan.do/teapot/external-dns
|
||||
k8s.gcr.io/external-dns/external-dns
|
||||
```
|
||||
|
||||
As tags, you can use your version of choice or use `latest` that always resolves to the latest tag.
|
||||
As tags, you use the external-dns release of choice(i.e. `v0.7.3`). A `latest` tag is not provided in the container registry.
|
||||
|
||||
If you wish to build your own image, you can use the provided [Dockerfile](../Dockerfile) as a starting point.
|
||||
|
||||
We are currently working with the Kubernetes community to provide official images for the project similarly to what is done with the other official Kubernetes projects, but we don't have an ETA on when those images will be available.
|
||||
### Which architectures are supported?
|
||||
|
||||
From `v0.7.5` on we support `amd64`, `arm32v7` and `arm64v8`. This means that you can run ExternalDNS on a Kubernetes cluster backed by Rasperry Pis or on ARM instances in the cloud as well as more traditional machines backed by `amd64` compatible CPUs.
|
||||
|
||||
### Which operating systems are supported?
|
||||
|
||||
At the time of writing we only support GNU/linux and we have no plans of supporting Windows or other operating systems.
|
||||
|
||||
### Why am I seeing time out errors even though I have connectivity to my cluster?
|
||||
|
||||
|
@ -2,10 +2,22 @@
|
||||
|
||||
## Release cycle
|
||||
|
||||
Currently we don't release regularly. Whenever we think it makes sense to release a new version we do it. You might want to ask in our Slack channel [external-dns](https://kubernetes.slack.com/archives/C771MKDKQ) when the next release will come out.
|
||||
Currently we don't release regularly. Whenever we think it makes sense to release a new version we do it, but we aim to do a new release every month. You might want to ask in our Slack channel [external-dns](https://kubernetes.slack.com/archives/C771MKDKQ) when the next release will come out.
|
||||
|
||||
## How to release a new image
|
||||
|
||||
When releasing a new version of external-dns, we tag the branch by using **vX.Y.Z** as tag name. This PR includes the updated **CHANGELOG.md** with the latest commits since last tag. As soon as we merge this PR into the default branch, Kubernetes based CI/CD system [Prow](https://prow.k8s.io/?repo=kubernetes-sigs%2Fexternal-dns) will trigger a job to push the image. We're using the Google Container Registry for our Docker images.
|
||||
### Prerequisite
|
||||
|
||||
The job itself looks at external-dns `cloudbuild.yaml` and executes the given steps. Inside it runs `make release.staging` which is basically only a `docker build` and `docker push`. The docker image is pushed `gcr.io/k8s-staging-external-dns/external-dns`, which is only a staging image and shouldn't be used. Promoting the official image we need to create another PR in [k8s.io](https://github.com/kubernetes/k8s.io), e.g. https://github.com/kubernetes/k8s.io/pull/540 by taking the current staging image using sha256.
|
||||
We use https://github.com/cli/cli to automate the release process. Please install it according to the [official documentation](https://github.com/cli/cli#installation).
|
||||
|
||||
You must be an official maintainer of the project to be able to do a release.
|
||||
|
||||
### Steps
|
||||
|
||||
- Run `scripts/releaser.sh` to create a new GitHub release.
|
||||
- The step above will trigger the Kubernetes based CI/CD system [Prow](https://prow.k8s.io/?repo=kubernetes-sigs%2Fexternal-dns). Verify that a new image was built and uploaded to `gcr.io/k8s-staging-external-dns/external-dns`.
|
||||
- Create a PR in the [k8s.io repo](https://github.com/kubernetes/k8s.io) (see https://github.com/kubernetes/k8s.io/pull/540 for reference) by taking the current staging image using the sha256 digest. Once the PR is merged, the image will be live with the corresponding tag specified in the PR.
|
||||
- Verify that the image is pullable with the given tag (i.e. `v0.7.5`).
|
||||
- Branch out from the default branch and run `scripts/kustomize-version-udapter.sh` to update the image tag used in the kustomization.yaml.
|
||||
- Create a PR with the kustomize change.
|
||||
- Once the PR is merged, all is done :-)
|
||||
|
@ -7,7 +7,7 @@ Akamai FastDNS provider support was added via [this PR](https://github.com/kuber
|
||||
The Akamai FastDNS provider expects that your zones, you wish to add records to, already exists
|
||||
and are configured correctly. It does not add, remove or configure new zones in anyway.
|
||||
|
||||
To do this pease refer to the [FastDNS documentation](https://learn.akamai.com/en-us/products/web_performance/fast_dns.html).
|
||||
To do this please refer to the [FastDNS documentation](https://learn.akamai.com/en-us/products/web_performance/fast_dns.html).
|
||||
|
||||
Additional data you will have to provide:
|
||||
|
||||
@ -18,7 +18,7 @@ Additional data you will have to provide:
|
||||
|
||||
Make these available to external DNS somehow. In the following example a secret is used by referencing the secret and its keys in the env section of the deployment.
|
||||
|
||||
If you happen to have questions regarding authentification, please refer to the [API Client Authentication documentation](https://developer.akamai.com/legacy/introduction/Client_Auth.html)
|
||||
If you happen to have questions regarding authentication, please refer to the [API Client Authentication documentation](https://developer.akamai.com/legacy/introduction/Client_Auth.html)
|
||||
|
||||
## Deployment
|
||||
|
||||
@ -49,7 +49,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.6.0
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress # or service or both
|
||||
- --provider=akamai
|
||||
|
@ -113,7 +113,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -187,7 +187,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -81,7 +81,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
env:
|
||||
- name: AWS_REGION
|
||||
value: us-east-1 # put your CloudMap NameSpace region
|
||||
|
@ -141,7 +141,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -216,7 +216,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -407,6 +407,13 @@ For any given DNS name, only **one** of the following routing policies can be us
|
||||
* `external-dns.alpha.kubernetes.io/aws-geolocation-subdivision-code`
|
||||
* Multi-value answer:`external-dns.alpha.kubernetes.io/aws-multi-value-answer`
|
||||
|
||||
## Associating DNS records with healthchecks
|
||||
|
||||
You can configure Route53 to associate DNS records with healthchecks for automated DNS failover using
|
||||
`external-dns.alpha.kubernetes.io/health-check-id: <health-check-id>` annotation.
|
||||
|
||||
Note: ExternalDNS does not support creating healthchecks, and assumes that `<health-check-id>` already exists.
|
||||
|
||||
## Clean up
|
||||
|
||||
Make sure to delete all Service objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
@ -420,3 +427,10 @@ Give ExternalDNS some time to clean up the DNS records for you. Then delete the
|
||||
```console
|
||||
$ aws route53 delete-hosted-zone --id /hostedzone/ZEWFWZ4R16P7IB
|
||||
```
|
||||
|
||||
## Throttling
|
||||
|
||||
Route53 has a [5 API requests per second per account hard quota](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-route-53).
|
||||
Running several fast polling ExternalDNS instances in a given account can easily hit that limit. Some ways to circumvent that issue includes:
|
||||
* Augment the synchronization interval (`--interval`), at the cost of slower changes propagation.
|
||||
* If the ExternalDNS managed zones list doesn't change frequently, set `--aws-zones-cache-duration` (zones list cache time-to-live) to a larger value. Note that zones list cache can be disabled with `--aws-zones-cache-duration=0s`.
|
||||
|
@ -167,7 +167,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -237,7 +237,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -307,7 +307,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -58,7 +58,7 @@ You can find the `subscriptionId` by running `az account show --query "id"` or b
|
||||
|
||||
The `resourceGroup` is the Resource Group created in a previous step.
|
||||
|
||||
The `aadClientID` and `aaClientSecret` are assoiated with the Service Principal, that you need to create next.
|
||||
The `aadClientID` and `aaClientSecret` are associated with the Service Principal, that you need to create next.
|
||||
|
||||
### Creating service principal
|
||||
A Service Principal with a minimum access level of `contributor` to the DNS zone(s) and `reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `contributor` to the resource group or the whole subscription).
|
||||
@ -191,7 +191,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -261,7 +261,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -331,7 +331,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -21,7 +21,9 @@ 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.
|
||||
|
||||
When using API Token authentication the token should be granted Zone `Read` and DNS `Edit` privileges.
|
||||
When using API Token authentication, the token should be granted Zone `Read`, DNS `Edit` privileges, and access to `All zones`.
|
||||
|
||||
If you would like to further restrict the API permissions to a specific zone (or zones), you also need to use the `--zone-id-filter` so that the underlying API requests only access the zones that you explicitly specify, as opposed to accessing all zones.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
@ -48,10 +50,11 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
- --zone-id-filter=023e105f4ecef8ad9ca31a8372d0c353 # (optional) limit to a specific zone.
|
||||
- --provider=cloudflare
|
||||
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
|
||||
env:
|
||||
@ -82,7 +85,7 @@ rules:
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
verbs: ["list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
@ -115,10 +118,11 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
- --zone-id-filter=023e105f4ecef8ad9ca31a8372d0c353 # (optional) limit to a specific zone.
|
||||
- --provider=cloudflare
|
||||
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
|
||||
env:
|
||||
|
@ -1,8 +1,13 @@
|
||||
# Configuring ExternalDNS to use the Contour IngressRoute Source
|
||||
This tutorial describes how to configure ExternalDNS to use the Contour IngressRoute source.
|
||||
It is meant to supplement the other provider-specific setup tutorials.
|
||||
# Setting up External DNS with Contour
|
||||
|
||||
This tutorial describes how to configure External DNS to use either the Contour `IngressRoute` or `HTTPProxy` source.
|
||||
The `IngressRoute` CRD is deprecated but still in-use in many clusters however it's recommended that you migrate to the `HTTPProxy` resource.
|
||||
Using the `HTTPProxy` resource with External DNS requires Contour version 1.5 or greater.
|
||||
|
||||
### Example manifests for External DNS
|
||||
#### Without RBAC
|
||||
Note that you don't need to enable both of the sources and if you don't enable `contour-ingressroute` you also don't need to configure the `contour-load-balancer` setting.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -21,12 +26,13 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --source=contour-ingressroute
|
||||
- --contour-load-balancer=custom-contour-namespace/custom-contour-lb # load balancer service to be used. Omit to use the default (heptio-contour/contour)
|
||||
- --source=contour-ingressroute # To enable IngressRoute support
|
||||
- --source=contour-httpproxy # To enable HTTPProxy support
|
||||
- --contour-load-balancer=custom-contour-namespace/custom-contour-lb # For IngressRoute ONLY: load balancer service to be used. Omit to use the default (heptio-contour/contour)
|
||||
- --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
|
||||
- --provider=aws
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
@ -35,7 +41,7 @@ spec:
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
#### With RBAC
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
@ -56,9 +62,14 @@ rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
# This section is only for IngressRoute
|
||||
- apiGroups: ["contour.heptio.com"]
|
||||
resources: ["ingressroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
# This section is only for HTTPProxy
|
||||
- apiGroups: ["projectcontour.io"]
|
||||
resources: ["httpproxies"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
@ -91,12 +102,13 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --source=contour-ingressroute
|
||||
- --contour-load-balancer=custom-contour-namespace/custom-contour-lb # load balancer service to be used. Omit to use the default (heptio-contour/contour)
|
||||
- --source=contour-ingressroute # To enable IngressRoute support
|
||||
- --source=contour-httpproxy # To enable HTTPProxy support
|
||||
- --contour-load-balancer=custom-contour-namespace/custom-contour-lb # For IngressRoute ONLY: load balancer service to be used. Omit to use the default (heptio-contour/contour)
|
||||
- --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
|
||||
- --provider=aws
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
@ -105,9 +117,9 @@ spec:
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
||||
### Verify External DNS works (IngressRoute example)
|
||||
### Verify External DNS works
|
||||
The following instructions are based on the
|
||||
[Contour example workload](https://github.com/heptio/contour/blob/HEAD/examples/example-workload/kuard-ingressroute.yaml).
|
||||
[Contour example workload](https://github.com/projectcontour/contour/tree/master/examples/example-workload/httpproxy).
|
||||
|
||||
#### Install a sample service
|
||||
```bash
|
||||
@ -147,7 +159,36 @@ spec:
|
||||
app: kuard
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
---
|
||||
EOF
|
||||
```
|
||||
|
||||
Then create either a `HTTPProxy` or an `IngressRoute`
|
||||
|
||||
#### HTTPProxy
|
||||
```
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: projectcontour.io/v1
|
||||
kind: HTTPProxy
|
||||
metadata:
|
||||
labels:
|
||||
app: kuard
|
||||
name: kuard
|
||||
namespace: default
|
||||
spec:
|
||||
virtualhost:
|
||||
fqdn: kuard.example.com
|
||||
routes:
|
||||
- conditions:
|
||||
- prefix: /
|
||||
services:
|
||||
- name: kuard
|
||||
port: 80
|
||||
EOF
|
||||
```
|
||||
|
||||
#### IngressRoute
|
||||
```
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: contour.heptio.com/v1beta1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
|
@ -108,7 +108,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
@ -175,7 +175,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
|
@ -59,7 +59,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
@ -133,9 +133,10 @@ spec:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
|
||||
@ -173,7 +173,7 @@ the DNSimple DNS records.
|
||||
|
||||
### Getting your DNSimple Account ID
|
||||
|
||||
If you do not know your DNSimple account ID it can be aquired using the [whoami](https://developer.dnsimple.com/v2/identity/#whoami) endpoint from the DNSimple Identity API
|
||||
If you do not know your DNSimple account ID it can be acquired using the [whoami](https://developer.dnsimple.com/v2/identity/#whoami) endpoint from the DNSimple Identity API
|
||||
|
||||
```sh
|
||||
curl -H "Authorization: Bearer $DNSIMPLE_ACCOUNT_TOKEN" \
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --txt-prefix=_d
|
||||
@ -130,7 +130,7 @@ spec:
|
||||
As the DNS name `test-ingress.example.com` matches the filter, external-dns will create two records:
|
||||
a CNAME for test-ingress.example.com and TXT for _dtest-ingress.example.com.
|
||||
|
||||
Create the Igress:
|
||||
Create the Ingress:
|
||||
|
||||
```
|
||||
$ kubectl create -f test-ingress.yaml
|
||||
|
@ -7,7 +7,7 @@ Exoscale provider support was added via [this PR](https://github.com/kubernetes-
|
||||
The Exoscale provider expects that your Exoscale zones, you wish to add records to, already exists
|
||||
and are configured correctly. It does not add, remove or configure new zones in anyway.
|
||||
|
||||
To do this pease refer to the [Exoscale DNS documentation](https://community.exoscale.com/documentation/dns/).
|
||||
To do this please refer to the [Exoscale DNS documentation](https://community.exoscale.com/documentation/dns/).
|
||||
|
||||
Additionally you will have to provide the Exoscale...:
|
||||
|
||||
@ -41,7 +41,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.6.0
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress # or service or both
|
||||
- --provider=exoscale
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage in conjunction with an ExternalName service.
|
||||
|
||||
## Usecases
|
||||
## Use cases
|
||||
|
||||
The main use cases that inspired this feature is the necessity for having a subdomain pointing to an external domain. In this scenario, it makes sense for the subdomain to have a CNAME record pointing to the external domain.
|
||||
|
||||
@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
|
@ -4,8 +4,6 @@ This tutorial describes how to setup ExternalDNS for usage within a GKE cluster.
|
||||
|
||||
## Set up your environment
|
||||
|
||||
*If you prefer to try-out ExternalDNS in one of the existing environments you can skip this step*
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.
|
||||
|
||||
```console
|
||||
@ -14,6 +12,16 @@ $ gcloud config set compute/region "europe-west1"
|
||||
$ gcloud config set compute/zone "europe-west1-d"
|
||||
```
|
||||
|
||||
## GKE Node Scopes
|
||||
|
||||
*If you prefer to try-out ExternalDNS in one of the existing environments you can skip this step*
|
||||
|
||||
The following instructions use instance scopes to provide ExternalDNS with the
|
||||
permissions it needs to manage DNS records. Note that since these permissions
|
||||
are associated with the instance, all pods in the cluster will also have these
|
||||
permissions. As such, this approach is not suitable for anything but testing
|
||||
environments.
|
||||
|
||||
Create a GKE cluster.
|
||||
|
||||
```console
|
||||
@ -52,58 +60,10 @@ $ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
## Deploy ExternalDNS
|
||||
### Deploy ExternalDNS
|
||||
|
||||
### Role-Based Access Control (RBAC)
|
||||
Then apply the following manifests file to deploy ExternalDNS.
|
||||
|
||||
[RBAC]("https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control") is enabled by default on all Container clusters which are running Kubernetes version 1.6 or higher.
|
||||
|
||||
Because of the way Container Engine checks permissions when you create a Role or ClusterRole, you must first create a RoleBinding that grants you all of the permissions included in the role you want to create.
|
||||
|
||||
```console
|
||||
kubectl create clusterrolebinding your-user-cluster-admin-binding --clusterrole=cluster-admin --user=your.google.cloud.email@example.org
|
||||
```
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created.
|
||||
|
||||
```console
|
||||
gcloud container clusters get-credentials "external-dns"
|
||||
```
|
||||
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
### 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
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --provider=google
|
||||
# - --google-project=zalando-external-dns-test # Use this to specify a project different from the one external-dns is running inside
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
@ -156,7 +116,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -170,8 +130,7 @@ spec:
|
||||
|
||||
Use `--dry-run` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.
|
||||
|
||||
|
||||
## Verify ExternalDNS works
|
||||
### Verify ExternalDNS works
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
@ -301,7 +260,7 @@ $ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
</html>
|
||||
```
|
||||
|
||||
## Clean up
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all Service and Ingress objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
|
||||
@ -326,5 +285,294 @@ $ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com.
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
### User Demo How-To Blogs and Examples
|
||||
## GKE with Workload Identity
|
||||
|
||||
The following instructions use [GKE workload
|
||||
identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||
to provide ExternalDNS with the permissions it needs to manage DNS records.
|
||||
Workload identity is the Google-recommended way to provide GKE workloads access
|
||||
to GCP APIs.
|
||||
|
||||
Create a GKE cluster with workload identity enabled.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters create external-dns \
|
||||
--workload-metadata-from-node=GKE_METADATA_SERVER \
|
||||
--identity-namespace=zalando-external-dns-test.svc.id.goog
|
||||
```
|
||||
|
||||
Create a GCP service account (GSA) for ExternalDNS and save its email address.
|
||||
|
||||
```console
|
||||
$ sa_name="Kubernetes external-dns"
|
||||
$ gcloud iam service-accounts create sa-edns --display-name="$sa_name"
|
||||
$ sa_email=$(gcloud iam service-accounts list --format='value(email)' \
|
||||
--filter="displayName:$sa_name")
|
||||
```
|
||||
|
||||
Bind the ExternalDNS GSA to the DNS admin role.
|
||||
|
||||
```console
|
||||
$ gcloud projects add-iam-policy-binding zalando-external-dns-test \
|
||||
--member="serviceAccount:$sa_email" --role=roles/dns.admin
|
||||
```
|
||||
|
||||
Link the ExternalDNS GSA to the Kubernetes service account (KSA) that
|
||||
external-dns will run under, i.e., the external-dns KSA in the external-dns
|
||||
namespaces.
|
||||
|
||||
```console
|
||||
$ gcloud iam service-accounts add-iam-policy-binding "$sa_email" \
|
||||
--member="serviceAccount:zalando-external-dns-test.svc.id.goog[external-dns/external-dns]" \
|
||||
--role=roles/iam.workloadIdentityUser
|
||||
```
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones create external-dns-test-gcp-zalan-do \
|
||||
--dns-name=external-dns-test.gcp.zalan.do. \
|
||||
--description="Automatically managed zone by ExternalDNS"
|
||||
```
|
||||
|
||||
Make a note of the nameservers that were assigned to your new zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone=external-dns-test-gcp-zalan-do \
|
||||
--name=external-dns-test.gcp.zalan.do. \
|
||||
--type NS
|
||||
NAME TYPE TTL DATA
|
||||
external-dns-test.gcp.zalan.do. NS 21600 ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
|
||||
```
|
||||
|
||||
In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could
|
||||
slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.
|
||||
|
||||
Tell the parent zone where to find the DNS records for this zone by adding the
|
||||
corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and
|
||||
the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the
|
||||
following.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created and bind your GCP
|
||||
user to the cluster admin role in Kubernetes.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters get-credentials external-dns
|
||||
$ kubectl create clusterrolebinding cluster-admin-me \
|
||||
--clusterrole=cluster-admin --user="$(gcloud config get-value account)"
|
||||
```
|
||||
|
||||
### Deploy ExternalDNS
|
||||
|
||||
Apply the following manifest file to deploy external-dns.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: 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: ["extensions", "networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["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: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --source=ingress
|
||||
- --source=service
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
- --provider=google
|
||||
- --google-project=zalando-external-dns-test
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
name: external-dns
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
runAsUser: 65534
|
||||
serviceAccountName: external-dns
|
||||
```
|
||||
|
||||
Then add the proper workload identity annotation to the cert-manager service
|
||||
account.
|
||||
|
||||
```bash
|
||||
$ kubectl annotate serviceaccount --namespace=external-dns external-dns \
|
||||
"iam.gke.io/gcp-service-account=$sa_email"
|
||||
```
|
||||
|
||||
### Deploy a sample application
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.gcp.zalan.do.
|
||||
name: nginx
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
After roughly two minutes check that a corresponding DNS records for your
|
||||
service and ingress were created.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "via-ingress.external-dns-test.gcp.zalan.do." \
|
||||
--type A
|
||||
NAME TYPE TTL DATA
|
||||
nginx.external-dns-test.gcp.zalan.do. A 300 104.155.60.49
|
||||
nginx.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
via-ingress.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
via-ingress.external-dns-test.gcp.zalan.do. A 300 35.187.1.246
|
||||
```
|
||||
|
||||
Let's check that we can resolve this DNS name as well.
|
||||
|
||||
```console
|
||||
$ dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
|
||||
35.187.1.246
|
||||
```
|
||||
|
||||
Try with `curl` as well.
|
||||
|
||||
```console
|
||||
$ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all service and ingress objects before terminating the
|
||||
cluster so all load balancers and DNS entries get cleaned up correctly.
|
||||
|
||||
```console
|
||||
$ kubectl delete ingress nginx
|
||||
$ kubectl delete service nginx
|
||||
```
|
||||
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the
|
||||
managed zone and cluster.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones delete external-dns-test-gcp-zalan-do
|
||||
$ gcloud container clusters delete external-dns
|
||||
```
|
||||
|
||||
Also delete the NS records for your removed zone from the parent zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
## User Demo How-To Blogs and Examples
|
||||
|
||||
* A full demo on GKE Kubernetes + CloudDNS + SA-Permissions [How-to Kubernetes with DNS management (ssl-manager pre-req)](https://medium.com/@jpantjsoha/how-to-kubernetes-with-dns-management-for-gitops-31239ea75d8d)
|
||||
* Run external-dns on GKE with workload identity. See [Kubernetes, ingress-nginx, cert-manager & external-dns](https://blog.atomist.com/kubernetes-ingress-nginx-cert-manager-external-dns/)
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.7.3
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage in conjunction with a Headless service.
|
||||
|
||||
## Usecases
|
||||
## Use cases
|
||||
The main use cases that inspired this feature is the necessity for fixed addressable hostnames with services, such as Kafka when trying to access them from outside the cluster. In this scenario, quite often, only the Node IP addresses are actually routable and as in systems like Kafka more direct connections are preferable.
|
||||
|
||||
## Setup
|
||||
@ -31,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
|
@ -69,7 +69,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains.
|
||||
@ -149,7 +149,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -98,7 +98,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -157,6 +157,7 @@ apiVersion: networking.istio.io/v1alpha3
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: httpbin-gateway
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
istio: ingressgateway # use Istio default gateway implementation
|
||||
|
@ -76,7 +76,7 @@ rules:
|
||||
```
|
||||
|
||||
See also current RBAC yaml files:
|
||||
- [kube-ingress-aws-controller](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/cluster/manifests/ingress-controller/rbac.yaml)
|
||||
- [kube-ingress-aws-controller](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/cluster/manifests/ingress-controller/01-rbac.yaml)
|
||||
- [skipper](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/cluster/manifests/skipper/rbac.yaml)
|
||||
- [external-dns](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/cluster/manifests/external-dns/rbac.yaml)
|
||||
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a GKE cluster that doesn't make use of Google's [default ingress controller](https://github.com/kubernetes/ingress-gce) but rather uses [nginx-ingress-controller](https://github.com/kubernetes/ingress-nginx) for that task.
|
||||
|
||||
## Set up your environment
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.
|
||||
|
||||
```console
|
||||
@ -10,6 +12,14 @@ $ gcloud config set compute/region "europe-west1"
|
||||
$ gcloud config set compute/zone "europe-west1-d"
|
||||
```
|
||||
|
||||
## GKE Node Scopes
|
||||
|
||||
The following instructions use instance scopes to provide ExternalDNS with the
|
||||
permissions it needs to manage DNS records. Note that since these permissions
|
||||
are associated with the instance, all pods in the cluster will also have these
|
||||
permissions. As such, this approach is not suitable for anything but testing
|
||||
environments.
|
||||
|
||||
Create a GKE cluster without using the default ingress controller.
|
||||
|
||||
```console
|
||||
@ -48,19 +58,20 @@ $ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of ExternalDNS will remove all records it doesn't recognize from the zone.
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created.
|
||||
Connect your `kubectl` client to the cluster you just created and bind your GCP
|
||||
user to the cluster admin role in Kubernetes.
|
||||
|
||||
```console
|
||||
gcloud container clusters get-credentials "external-dns"
|
||||
$ gcloud container clusters get-credentials "external-dns"
|
||||
$ kubectl create clusterrolebinding cluster-admin-me \
|
||||
--clusterrole=cluster-admin --user="$(gcloud config get-value account)"
|
||||
```
|
||||
|
||||
## Deploy the nginx ingress controller
|
||||
### Deploy the nginx ingress controller
|
||||
|
||||
First, you need to deploy the nginx-based ingress controller. It can be deployed in at least two modes: Leveraging a Layer 4 load balancer in front of the nginx proxies or directly targeting pods with hostPorts on your worker nodes. ExternalDNS doesn't really care and supports both modes.
|
||||
|
||||
### Default Backend
|
||||
#### Default Backend
|
||||
|
||||
The nginx controller uses a default backend that it serves when no Ingress rule matches. This is a separate Service that can be picked by you. We'll use the default backend that's used by other ingress controllers for that matter. Apply the following manifests to your cluster to deploy the default backend.
|
||||
|
||||
@ -96,7 +107,7 @@ spec:
|
||||
image: gcr.io/google_containers/defaultbackend:1.3
|
||||
```
|
||||
|
||||
### Without a separate TCP load balancer
|
||||
#### Without a separate TCP load balancer
|
||||
|
||||
By default, the controller will update your Ingress objects with the public IPs of the nodes running your nginx controller instances. You should run multiple instances in case of pod or node failure. The controller will do leader election and will put multiple IPs as targets in your Ingress objects in that case. It could also make sense to run it as a DaemonSet. However, we'll just run a single replica. You have to open the respective ports on all of your worker nodes to allow nginx to receive traffic.
|
||||
|
||||
@ -145,7 +156,7 @@ spec:
|
||||
hostPort: 443
|
||||
```
|
||||
|
||||
### With a separate TCP load balancer
|
||||
#### With a separate TCP load balancer
|
||||
|
||||
However, you can also have the ingress controller proxied by a Kubernetes Service. This will instruct the controller to populate this Service's external IP as the external IP of the Ingress. This exposes the nginx proxies via a Layer 4 load balancer (`type=LoadBalancer`) which is more reliable than the other method. With that approach, you can run as many nginx proxy instances on your cluster as you like or have them autoscaled. This is the preferred way of running the nginx controller.
|
||||
|
||||
@ -206,7 +217,7 @@ spec:
|
||||
- containerPort: 443
|
||||
```
|
||||
|
||||
## Deploy ExternalDNS
|
||||
### Deploy ExternalDNS
|
||||
|
||||
Apply the following manifest file to deploy ExternalDNS.
|
||||
|
||||
@ -262,7 +273,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
@ -274,7 +285,7 @@ spec:
|
||||
|
||||
Use `--dry-run` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.
|
||||
|
||||
## Deploy a sample application
|
||||
### Deploy a sample application
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
@ -363,7 +374,7 @@ $ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
</html>
|
||||
```
|
||||
|
||||
## Clean up
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all Service and Ingress objects before terminating the cluster so all load balancers and DNS entries get cleaned up correctly.
|
||||
|
||||
@ -387,3 +398,302 @@ $ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com.
|
||||
--name "external-dns-test.gcp.zalan.do." --ttl 300 --type NS --zone "gcp-zalan-do"
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
## GKE with Workload Identity
|
||||
|
||||
The following instructions use [GKE workload
|
||||
identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||
to provide ExternalDNS with the permissions it needs to manage DNS records.
|
||||
Workload identity is the Google-recommended way to provide GKE workloads access
|
||||
to GCP APIs.
|
||||
|
||||
Create a GKE cluster with workload identity enabled and without the
|
||||
HttpLoadBalancing add-on.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters create external-dns \
|
||||
--workload-metadata-from-node=GKE_METADATA_SERVER \
|
||||
--identity-namespace=zalando-external-dns-test.svc.id.goog \
|
||||
--addons=HorizontalPodAutoscaling
|
||||
```
|
||||
|
||||
Create a GCP service account (GSA) for ExternalDNS and save its email address.
|
||||
|
||||
```console
|
||||
$ sa_name="Kubernetes external-dns"
|
||||
$ gcloud iam service-accounts create sa-edns --display-name="$sa_name"
|
||||
$ sa_email=$(gcloud iam service-accounts list --format='value(email)' \
|
||||
--filter="displayName:$sa_name")
|
||||
```
|
||||
|
||||
Bind the ExternalDNS GSA to the DNS admin role.
|
||||
|
||||
```console
|
||||
$ gcloud projects add-iam-policy-binding zalando-external-dns-test \
|
||||
--member="serviceAccount:$sa_email" --role=roles/dns.admin
|
||||
```
|
||||
|
||||
Link the ExternalDNS GSA to the Kubernetes service account (KSA) that
|
||||
external-dns will run under, i.e., the external-dns KSA in the external-dns
|
||||
namespaces.
|
||||
|
||||
```console
|
||||
$ gcloud iam service-accounts add-iam-policy-binding "$sa_email" \
|
||||
--member="serviceAccount:zalando-external-dns-test.svc.id.goog[external-dns/external-dns]" \
|
||||
--role=roles/iam.workloadIdentityUser
|
||||
```
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones create external-dns-test-gcp-zalan-do \
|
||||
--dns-name=external-dns-test.gcp.zalan.do. \
|
||||
--description="Automatically managed zone by ExternalDNS"
|
||||
```
|
||||
|
||||
Make a note of the nameservers that were assigned to your new zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone=external-dns-test-gcp-zalan-do \
|
||||
--name=external-dns-test.gcp.zalan.do. \
|
||||
--type NS
|
||||
NAME TYPE TTL DATA
|
||||
external-dns-test.gcp.zalan.do. NS 21600 ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
|
||||
```
|
||||
|
||||
In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could
|
||||
slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.
|
||||
|
||||
Tell the parent zone where to find the DNS records for this zone by adding the
|
||||
corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and
|
||||
the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the
|
||||
following.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created and bind your GCP
|
||||
user to the cluster admin role in Kubernetes.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters get-credentials external-dns
|
||||
$ kubectl create clusterrolebinding cluster-admin-me \
|
||||
--clusterrole=cluster-admin --user="$(gcloud config get-value account)"
|
||||
```
|
||||
|
||||
### Deploy ingress-nginx
|
||||
|
||||
Follow the [ingress-nginx GKE installation
|
||||
instructions](https://kubernetes.github.io/ingress-nginx/deploy/#gce-gke) to
|
||||
deploy it to the cluster.
|
||||
|
||||
```console
|
||||
$ kubectl apply -f \
|
||||
https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.35.0/deploy/static/provider/cloud/deploy.yaml
|
||||
```
|
||||
|
||||
### Deploy ExternalDNS
|
||||
|
||||
Apply the following manifest file to deploy external-dns.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: 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: ["extensions", "networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["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: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
- --provider=google
|
||||
- --google-project=zalando-external-dns-test
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
name: external-dns
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
runAsUser: 65534
|
||||
serviceAccountName: external-dns
|
||||
```
|
||||
|
||||
Then add the proper workload identity annotation to the cert-manager service
|
||||
account.
|
||||
|
||||
```bash
|
||||
$ kubectl annotate serviceaccount --namespace=external-dns external-dns \
|
||||
"iam.gke.io/gcp-service-account=$sa_email"
|
||||
```
|
||||
|
||||
### Deploy a sample application
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
After roughly two minutes check that a corresponding DNS record for your ingress
|
||||
was created.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "via-ingress.external-dns-test.gcp.zalan.do." \
|
||||
--type A
|
||||
NAME TYPE TTL DATA
|
||||
via-ingress.external-dns-test.gcp.zalan.do. A 300 35.187.1.246
|
||||
```
|
||||
|
||||
Let's check that we can resolve this DNS name as well.
|
||||
|
||||
```console
|
||||
$ dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
|
||||
35.187.1.246
|
||||
```
|
||||
|
||||
Try with `curl` as well.
|
||||
|
||||
```console
|
||||
$ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all service and ingress objects before terminating the
|
||||
cluster so all load balancers and DNS entries get cleaned up correctly.
|
||||
|
||||
```console
|
||||
$ kubectl delete service --namespace=ingress-nginx ingress-nginx-controller
|
||||
$ kubectl delete ingress nginx
|
||||
```
|
||||
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the
|
||||
managed zone and cluster.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones delete external-dns-test-gcp-zalan-do
|
||||
$ gcloud container clusters delete external-dns
|
||||
```
|
||||
|
||||
Also delete the NS records for your removed zone from the parent zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
## User Demo How-To Blogs and Examples
|
||||
|
||||
* Run external-dns on GKE with workload identity. See [Kubernetes, ingress-nginx, cert-manager & external-dns](https://blog.atomist.com/kubernetes-ingress-nginx-cert-manager-external-dns/)
|
||||
|
23
docs/tutorials/ns-record.md
Normal file
23
docs/tutorials/ns-record.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Creating NS record with CRD source
|
||||
|
||||
You can create NS records with the help of [CRD source](/docs/contributing/crd-source.md)
|
||||
and `DNSEndpoint` CRD.
|
||||
|
||||
Consider the following example
|
||||
|
||||
```yaml
|
||||
apiVersion: externaldns.k8s.io/v1alpha1
|
||||
kind: DNSEndpoint
|
||||
metadata:
|
||||
name: ns-record
|
||||
spec:
|
||||
endpoints:
|
||||
- dnsName: zone.example.com
|
||||
recordTTL: 300
|
||||
recordType: NS
|
||||
targets:
|
||||
- ns1.example.com
|
||||
- ns2.example.com
|
||||
```
|
||||
|
||||
After instantiation of this Custom Resource external-dns will create NS record with the help of configured provider, e.g. `aws`
|
@ -61,7 +61,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
|
@ -25,7 +25,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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
|
||||
@ -92,7 +92,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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
|
||||
|
@ -26,6 +26,8 @@ auth:
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
-----END RSA PRIVATE KEY-----
|
||||
fingerprint: af:81:71:8e...
|
||||
# Omit if there is not a password for the key
|
||||
passphrase: Tx1jRk...
|
||||
compartment: ocid1.compartment.oc1...
|
||||
```
|
||||
|
||||
@ -91,12 +93,12 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --provider=oci
|
||||
- --policy=upsert-only # prevent ExternalDNSfrom deleting any records, omit to enable full synchronization
|
||||
- --policy=upsert-only # prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
- --txt-owner-id=my-identifier
|
||||
volumeMounts:
|
||||
- name: config
|
||||
|
@ -86,7 +86,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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,6 +125,9 @@ rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
@ -157,7 +160,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=pdns
|
||||
|
@ -243,7 +243,7 @@ spec:
|
||||
- --txt-owner-id=external-dns
|
||||
- --annotation-filter=kubernetes.io/ingress.class=external-ingress
|
||||
- --aws-zone-type=public
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
name: external-dns-public
|
||||
```
|
||||
|
||||
@ -281,7 +281,7 @@ spec:
|
||||
- --txt-owner-id=dev.k8s.nexus
|
||||
- --annotation-filter=kubernetes.io/ingress.class=internal-ingress
|
||||
- --aws-zone-type=private
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
name: external-dns-private
|
||||
```
|
||||
|
||||
|
@ -53,7 +53,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
@ -123,7 +123,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
|
@ -108,9 +108,29 @@ spec:
|
||||
serviceName: my-service
|
||||
servicePort: 8000
|
||||
```
|
||||
There are other annotation that can affect the generation of DNS records like
|
||||
external-dns.alpha.kubernetes.io/ttl. These are beyond the scope of this
|
||||
tutorial and are covered elsewhere in the docs.
|
||||
|
||||
### Custom TTL
|
||||
|
||||
The default DNS record TTL (Time-To-Live) is 0 seconds. You can customize this value by setting the annotation `external-dns.alpha.kubernetes.io/ttl`. e.g., modify the service manifest YAML file above:
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com
|
||||
external-dns.alpha.kubernetes.io/ttl: 60
|
||||
spec:
|
||||
...
|
||||
```
|
||||
|
||||
This will set the DNS record's TTL to 60 seconds.
|
||||
|
||||
A default TTL for all records can be set using the the flag with a time in seconds, minutes or hours, such as `--rfc2136-min-ttl=60s`
|
||||
|
||||
There are other annotation that can affect the generation of DNS records, but these are beyond the scope of this
|
||||
tutorial and are covered in the main documentation.
|
||||
|
||||
### Test with external-dns installed on local machine (optional)
|
||||
You may install external-dns and test on a local machine by running:
|
||||
@ -197,7 +217,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.6.0
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --txt-owner-id=k8s
|
||||
- --provider=rfc2136
|
||||
@ -237,7 +257,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.6.0
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --txt-owner-id=k8s
|
||||
- --provider=rfc2136
|
||||
|
209
docs/tutorials/scaleway.md
Normal file
209
docs/tutorials/scaleway.md
Normal file
@ -0,0 +1,209 @@
|
||||
# Setting up ExternalDNS for Services on Scaleway
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Scaleway DNS.
|
||||
|
||||
Make sure to use **>=0.7.4** version of ExternalDNS for this tutorial.
|
||||
|
||||
**Warning**: Scaleway DNS is currently in Public Beta and may not be suited for production usage.
|
||||
|
||||
## Importing a Domain into Scaleway DNS
|
||||
|
||||
In order to use your domain, you need to import it into Scaleway DNS. If it's not already done, you can follow [this documentation](https://www.scaleway.com/en/docs/scaleway-dns/)
|
||||
|
||||
Once the domain is imported you can either use the root zone, or create a subzone to use.
|
||||
|
||||
In this example we will use `example.com` as an example.
|
||||
|
||||
## Creating Scaleway Credentials
|
||||
|
||||
To use ExternalDNS with Scaleway DNS, you need to create an API token (composed of the Access Key and the Secret Key).
|
||||
You can either use existing ones or you can create a new token, as explained in [How to generate an API token](https://www.scaleway.com/en/docs/generate-an-api-token/) or directly by going to the [credentials page](https://console.scaleway.com/account/organization/credentials).
|
||||
|
||||
Note that you will also need to the Organization ID, which can be retrieve on the same page.
|
||||
|
||||
Three environment variables are needed to run ExternalDNS with Scaleway DNS:
|
||||
- `SCW_ACCESS_KEY` which is the Access Key.
|
||||
- `SCW_SECRET_KEY` which is the Secret Key.
|
||||
- `SCW_DEFAULT_ORGANIZATION_ID` which is your Organization ID.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
The following example are suited for development. For a production usage, prefer secrets over environment, and use a [tagged release](https://github.com/kubernetes-sigs/external-dns/releases).
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.4
|
||||
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.
|
||||
- --provider=scaleway
|
||||
env:
|
||||
- name: SCW_ACCESS_KEY
|
||||
value: "<your access key>"
|
||||
- name: SCW_SECRET_KEY
|
||||
value: "<your secret key>"
|
||||
- name: SCW_DEFAULT_ORGANIZATION_ID
|
||||
value: "<your organization ID>"
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
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:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.4
|
||||
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.
|
||||
- --provider=scaleway
|
||||
env:
|
||||
- name: SCW_ACCESS_KEY
|
||||
value: "<your access key>"
|
||||
- name: SCW_SECRET_KEY
|
||||
value: "<your secret key>"
|
||||
- name: SCW_DEFAULT_ORGANIZATION_ID
|
||||
value: "<your organization ID>"
|
||||
```
|
||||
|
||||
|
||||
## Deploying an Nginx Service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: my-app.example.com
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
Note the annotation on the service; use the same hostname as the Scaleway DNS zone created above.
|
||||
|
||||
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation will cause ExternalDNS to remove the corresponding DNS records.
|
||||
|
||||
Create the deployment and service:
|
||||
|
||||
```console
|
||||
$ kubectl create -f nginx.yaml
|
||||
```
|
||||
|
||||
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
|
||||
|
||||
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize the Scaleway DNS records.
|
||||
|
||||
## Verifying Scaleway DNS records
|
||||
|
||||
Check your [Scaleway DNS UI](https://console.scaleway.com/domains/external) to view the records for your Scaleway DNS zone.
|
||||
|
||||
Click on the zone for the one created above if a different domain was used.
|
||||
|
||||
This should show the external IP address of the service as the A record for your domain.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Now that we have verified that ExternalDNS will automatically manage Scaleway DNS records, we can delete the tutorial's example:
|
||||
|
||||
```
|
||||
$ kubectl delete service -f nginx.yaml
|
||||
$ kubectl delete service -f externaldns.yaml
|
||||
```
|
@ -20,7 +20,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: eu.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.6.0 # minimum version is v0.5.6
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- ... # your arguments here
|
||||
securityContext:
|
||||
|
@ -36,7 +36,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress # ingress is also possible
|
||||
@ -116,7 +116,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -66,7 +66,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
@ -137,7 +137,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
|
@ -42,7 +42,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.opensource.zalan.do/teapot/external-dns:latest
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.3
|
||||
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.
|
||||
@ -116,7 +116,7 @@ spec:
|
||||
value: "YOU_VULTR_API_KEY"
|
||||
```
|
||||
|
||||
## Deploying an Nginx Service
|
||||
## Deploying a Nginx Service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
|
||||
|
@ -33,6 +33,8 @@ const (
|
||||
RecordTypeTXT = "TXT"
|
||||
// RecordTypeSRV is a RecordType enum value
|
||||
RecordTypeSRV = "SRV"
|
||||
// RecordTypeNS is a RecordType enum value
|
||||
RecordTypeNS = "NS"
|
||||
)
|
||||
|
||||
// TTL is a structure defining the TTL of a DNS record
|
||||
@ -85,7 +87,7 @@ func (t Targets) Same(o Targets) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsLess should fulfill the requirement to compare two targets and chosse the 'lesser' one.
|
||||
// IsLess should fulfill the requirement to compare two targets and choose the 'lesser' one.
|
||||
// In the past target was a simple string so simple string comparison could be used. Now we define 'less'
|
||||
// as either being the shorter list of targets or where the first entry is less.
|
||||
// FIXME We really need to define under which circumstances a list Targets is considered 'less'
|
||||
|
36
go.mod
36
go.mod
@ -1,15 +1,15 @@
|
||||
module sigs.k8s.io/external-dns
|
||||
|
||||
go 1.14
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.50.0
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200525170043-def10a4a28e0
|
||||
github.com/Azure/azure-sdk-for-go v36.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.9.4
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.3
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.0.0-00010101000000-000000000000
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d
|
||||
github.com/Azure/azure-sdk-for-go v45.1.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.10
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||
github.com/alecthomas/colour v0.1.0 // indirect
|
||||
@ -17,7 +17,6 @@ require (
|
||||
github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357
|
||||
github.com/aws/aws-sdk-go v1.31.4
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cloudflare/cloudflare-go v0.10.1
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
|
||||
@ -26,13 +25,10 @@ require (
|
||||
github.com/exoscale/egoscale v0.18.1
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
|
||||
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
github.com/google/go-cmp v0.4.1
|
||||
github.com/gophercloud/gophercloud v0.1.0
|
||||
github.com/gorilla/mux v1.7.4 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/heptio/contour v0.15.0
|
||||
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65
|
||||
github.com/linki/instrumented_http v0.2.0
|
||||
github.com/linode/linodego v0.19.0
|
||||
@ -48,18 +44,12 @@ require (
|
||||
github.com/projectcontour/contour v1.5.0
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/smartystreets/assertions v1.0.1 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/smartystreets/gunit v1.3.4 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/terra-farm/udnssdk v1.3.5 // indirect
|
||||
github.com/transip/gotransip v5.8.2+incompatible
|
||||
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556
|
||||
github.com/vultr/govultr v0.4.2
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875
|
||||
@ -71,16 +61,12 @@ require (
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc
|
||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751
|
||||
k8s.io/api v0.18.3
|
||||
k8s.io/apimachinery v0.18.3
|
||||
k8s.io/client-go v0.18.3
|
||||
k8s.io/api v0.18.8
|
||||
k8s.io/apimachinery v0.18.8
|
||||
k8s.io/client-go v0.18.8
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.0.1+incompatible
|
||||
github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.9.1
|
||||
github.com/Azure/go-autorest/autorest/adal => github.com/Azure/go-autorest/autorest/adal v0.6.0
|
||||
github.com/Azure/go-autorest/autorest/azure/auth => github.com/Azure/go-autorest/autorest/azure/auth v0.3.0
|
||||
github.com/golang/glog => github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d
|
||||
// TODO(jpg): Pin gRPC to work around breaking change until all dependences are upgraded: https://github.com/etcd-io/etcd/issues/11563
|
||||
google.golang.org/grpc => google.golang.org/grpc v1.26.0
|
||||
|
272
go.sum
272
go.sum
@ -1,5 +1,4 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
@ -14,32 +13,39 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
|
||||
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
|
||||
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200525170043-def10a4a28e0 h1:kdxglEveTcqIG5zEPdQ0Y5KctnIGR7zXsQCQakoTNxU=
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200525170043-def10a4a28e0/go.mod h1:n26Twiii5jhkMC+Ocz/s8R73cBBcXRIwyTqQ+6bOZGo=
|
||||
github.com/Azure/azure-sdk-for-go v36.0.0+incompatible h1:XIaBmA4pgKqQ7jInQPaNJQ4pOHrdJjw9gYXhbyiChaU=
|
||||
github.com/Azure/azure-sdk-for-go v36.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d h1:d6sdozgfqtgaOhjUn++lbo5siX3HELjcOUnbtrvVQi4=
|
||||
git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d/go.mod h1:n26Twiii5jhkMC+Ocz/s8R73cBBcXRIwyTqQ+6bOZGo=
|
||||
github.com/Azure/azure-sdk-for-go v45.1.0+incompatible h1:kxtaPD8n2z5Za+9e3sKsYG2IX6PG2R6VXtgS7gAbh3A=
|
||||
github.com/Azure/azure-sdk-for-go v45.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v13.0.1+incompatible h1:wRg6hB3T3dp7qjj5v3NmVsdU9IyXodW+SQnN9xlpGEA=
|
||||
github.com/Azure/go-autorest v13.0.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.1 h1:JB7Mqhna/7J8gZfVHjxDSTLSD6ciz2YgSMb/4qLXTtY=
|
||||
github.com/Azure/go-autorest/autorest v0.9.1/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.6.0 h1:UCTq22yE3RPgbU/8u4scfnnzuCW6pwQ9n+uBtV78ouo=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.6.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.3.0 h1:JwftqZDtWkr3qt1kcEgPd7H57uCHsXKXf66agWUQcGw=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.3.0/go.mod h1:CI4BQYBct8NS7BXNBBX+RchsFsUu5+oz+OSyR/ZIi7U=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.0 h1:5PAqnv+CSTwW9mlZWZAizmzrazFWEgZykEZXpr2hDtY=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.0/go.mod h1:rNYMNAefZMRowqCV0cVhr/YDW5dD7afFq9nXAXL4ykE=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.11.9/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.10 h1:j5sGbX7uj1ieYYkQ3Mpvewd4DCsEQ+ZeJpqnSM9pjnM=
|
||||
github.com/Azure/go-autorest/autorest v0.11.10/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3 h1:lZifaPRAk1bqg5vGqreL6F8uLC5V0fDpY8nFvc3boFc=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3/go.mod h1:4bJZhUhcq8LB20TruwHbAQsmUs2Xh+QR7utuJpLXX3A=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||
@ -50,11 +56,7 @@ github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5 h1:OU+AFpBEhyclrQGx4I6zpCx5WvXiKqvFeeOASOmhKCY=
|
||||
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11 h1:QGjNHMwoPYxE5NpOAc8kpd2KTY293/oFk5BWdjkza+k=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11/go.mod h1:L+HB2uBoDgi3+r1pJEJcbGwyyHhd2QXaGsKLbDwtm8Q=
|
||||
@ -72,15 +74,9 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v0.0.0-20180201100744-9d52b1fc8da9/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f h1:hinXH9rcBjRoIih5tl4f1BCbNjOmPJ2UnZwcYDhEHR0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357 h1:3ynCSeUh9OtJLd/OzLapM1DLDv2g+0yyDdkLqSfZCaQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
@ -91,22 +87,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/cactus/go-statsd-client v3.1.1+incompatible/go.mod h1:cMRcwZDklk7hXp+Law83urTHUiHMzCev/r4JMYr/zU0=
|
||||
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
@ -141,7 +123,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/godo v1.36.0 h1:eRF8wNzHZyU7/wI3De/MQgiVSWdseDaf27bXj2gnOO0=
|
||||
@ -160,26 +141,14 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.8.2/go.mod h1:EWRTAFN6uuDZIa6KOuUfrOMJ7ySgXZ44rVKiTWjKe34=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0 h1:67WMNTvGrl7V1dWdKCeTwxDr7nio9clKoTlLhwIPnT4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
|
||||
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.0.0-20190405222122-d6164de49109/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@ -190,6 +159,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 h1:jmwW6QWvUO2OPe22YfgFvBaaZlSr8Rlrac5lZvG6IdM=
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99/go.mod h1:4mP9w9+vYGw2jUx2+2v03IA+phyQQjNRR4AL3uxlNrs=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@ -260,10 +231,7 @@ github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
@ -272,9 +240,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@ -283,11 +250,7 @@ github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@ -297,7 +260,6 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f h1:kSqKc8ouCLIBHqdj9a9xxhtxlZhNqbePClixA4HoM44=
|
||||
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:YCHYtYb9c8Q7XgYVYjmJBPtFPKx5QvOcPxHZWjldabE=
|
||||
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -306,16 +268,10 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v15.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@ -331,19 +287,13 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
@ -367,16 +317,11 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/heptio/contour v0.15.0/go.mod h1:y4LmuX+86v8mlRd1HVrb2u4t77jMjOQ3DnjfRCiwrfA=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
@ -384,15 +329,13 @@ github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65 h1:FP5rOFP4ifbtFIjFHJmwhFrsbDyONILK/FNntl/Pou8=
|
||||
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -408,7 +351,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -423,13 +365,8 @@ github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46Otdh
|
||||
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA=
|
||||
github.com/linki/instrumented_http v0.2.0 h1:zLhcB3Q/McQQqml3qd5kzdZ0cGnL3vquPFIW2338f5Y=
|
||||
github.com/linki/instrumented_http v0.2.0/go.mod h1:pjYbItoegfuVi2GUOMhEqzvm/SJKuEL3H0tc8QRLRFk=
|
||||
github.com/linode/linodego v0.15.0 h1:hIPphfUvQlheBEV2YbTQQ1KUPE5LPe0EDHvoySwuiu4=
|
||||
github.com/linode/linodego v0.15.0/go.mod h1:vlzb2glsL9XrRYTRJ5JrgUoKZ5yfZBe11GYfEB68McY=
|
||||
github.com/linode/linodego v0.19.0 h1:JxYBTxUcXcOlCwLMuugc7Il0RMtJ7riaddqz6gG/ACA=
|
||||
github.com/linode/linodego v0.19.0 h1:JxYBTxUcXcOlCwLMuugc7Il0RMtJ7riaddqz6gG/ACA=
|
||||
github.com/linode/linodego v0.19.0/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
|
||||
github.com/linode/linodego v0.19.0/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
|
||||
github.com/lyft/protoc-gen-star v0.4.10/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgocG1206xAFPcs94=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -450,9 +387,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/maxatome/go-testdeep v1.4.0 h1:vKQh3/lHKAMsxggya/fXB6fLbf70c7k6wlLveuS9sKE=
|
||||
github.com/maxatome/go-testdeep v1.4.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
|
||||
github.com/mdempsky/unconvert v0.0.0-20190325185700-2f5dc3378ed3/go.mod h1:9+3Wp2ccIz73BJqVfc7n2+1A+mzvnEwtDTqEjeRngBQ=
|
||||
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
|
||||
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b h1:5f5B1kp+QerGOF91q1qVJcUWWvXsVEN3OKiyEzAAjIM=
|
||||
@ -482,40 +416,22 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/openshift/api v0.0.0-20200116145750-0e2ff1e215dd/go.mod h1:fT6U/JfG8uZzemTRwZA2kBDJP5nWz7v05UHnty/D+pk=
|
||||
github.com/openshift/api v0.0.0-20200302134843-001335d6cc34 h1:dqL8/YLrv/n4E5JBBkYPU/jzLQLLP5YZpsKAfh8CtNI=
|
||||
github.com/openshift/api v0.0.0-20200302134843-001335d6cc34/go.mod h1:frTMT4l3rOMlXj3ClYgKxgkq24D7IKXb3Bl4vJEewJw=
|
||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae h1:cRqNH6AtRQwEqCpymMkaR2ePp08FBIYLkU7YusJeZJ8=
|
||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae h1:cRqNH6AtRQwEqCpymMkaR2ePp08FBIYLkU7YusJeZJ8=
|
||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92DrZBuWMNKcot1iZUHfbYSJyBWHGgg6Dn6s=
|
||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92DrZBuWMNKcot1iZUHfbYSJyBWHGgg6Dn6s=
|
||||
github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
|
||||
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
|
||||
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
|
||||
github.com/openshift/client-go v0.0.0-20200116145930-eb24d03d8420 h1:+0HMnbsn4odRTirQB5ImG2w13yH6vj1MI2WD0+wPDaI=
|
||||
github.com/openshift/client-go v0.0.0-20200116145930-eb24d03d8420/go.mod h1:4riOwdj99Hd/q+iAcJZfNCsQQQMwURnZV6RL4WHYS5w=
|
||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
|
||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
|
||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
|
||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs=
|
||||
github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/oracle/oci-go-sdk v21.4.0+incompatible h1:ORX+RXBuG/INBs+rgx6S3qoShEZ5+rwEEyRn2s6bPiw=
|
||||
github.com/oracle/oci-go-sdk v21.4.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c=
|
||||
@ -527,7 +443,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -540,39 +455,24 @@ github.com/projectcontour/contour v1.5.0 h1:4zz4XWKKb1Nk2zXVQ27ZpoTivjG2DQvYLwrS
|
||||
github.com/projectcontour/contour v1.5.0/go.mod h1:y1MEsorL/Q8lBG5BZz8Gzryi9L5ryVALOuHicmAdfW8=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190403104016-ea9eea638872/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/prom2json v1.1.0/go.mod h1:v7OY1795b9fEUZgq4UU2+15YjRv0LfpxKejIQCy3L7o=
|
||||
github.com/prometheus/prom2json v1.2.1/go.mod h1:yIcXOj/TLPdtZ12qRyhswPnu+02sfDoqatDjj0WGSvo=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
@ -582,17 +482,17 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 h1:vOcHdR1nu7DO4BAx1rwzdHV7jQTzW3gqcBT5qxHSc6A=
|
||||
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0/go.mod h1:FeplEtXXejBYC4NPAFTrs5L7KuK+5RL9bf5nB2vZe9o=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301 h1:qj0du14RIOnmePII/eTlw1aHKDYL6zxDIk/Dq7Tef9k=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
@ -602,10 +502,8 @@ github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:X
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/gunit v1.1.1/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||
github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA=
|
||||
github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||
github.com/smartystreets/gunit v1.3.4 h1:iHc8Rfhb/uCOc9a3KGuD3ut22L+hLIVaqR1o5fS6zC4=
|
||||
github.com/smartystreets/gunit v1.3.4/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
@ -631,18 +529,14 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A=
|
||||
github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I=
|
||||
github.com/terra-farm/udnssdk v1.3.5/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip v5.8.2+incompatible h1:aNJhw/w/3QBqFcHAIPz1ytoK5FexeMzbUCGrrhWr3H0=
|
||||
github.com/transip/gotransip v5.8.2+incompatible/go.mod h1:uacMoJVmrfOcscM4Bi5NVg708b7c6rz2oDTWqa7i2Ic=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 h1:n7unetnX8WWTc0U85h/0+dJoLWLqoaJwowXB9RkBdxU=
|
||||
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60/go.mod h1:43vmy6GEvRuVMpGEWfJ/JoEM6RIqUQI1/tb8JqZR1zI=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
@ -666,12 +560,8 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875 h1:C7kWARE8r64ppRadl40yfNo6pag+G6ocvGU2xZ6yNes=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@ -688,20 +578,19 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -731,7 +620,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -755,7 +643,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -772,35 +659,26 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -808,16 +686,13 @@ golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fq
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -828,7 +703,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190325161752-5a8dccf5b48a/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@ -837,8 +711,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -856,7 +728,6 @@ gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3m
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@ -871,7 +742,6 @@ google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpC
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@ -880,16 +750,6 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@ -935,99 +795,60 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
istio.io/api v0.0.0-20200324230230-11f0c7211ae4/go.mod h1:bcY3prusO/6vA6zGHz4PNG2v79clPyTw06Xx3fprJSQ=
|
||||
istio.io/api v0.0.0-20200324230725-4b064f75ad8f h1:KIE2M1/XiG8YWNrk1Wkcp8cfWbZ0lDihUqtBCmlrMH0=
|
||||
istio.io/api v0.0.0-20200324230725-4b064f75ad8f/go.mod h1:bcY3prusO/6vA6zGHz4PNG2v79clPyTw06Xx3fprJSQ=
|
||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc h1:cR9GmbIBAz3FnY3tgs1SRn/uiznhtvG+mZBfD1p2vIA=
|
||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc h1:cR9GmbIBAz3FnY3tgs1SRn/uiznhtvG+mZBfD1p2vIA=
|
||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc/go.mod h1:kyq3g5w42zl/AKlbzDGppYpGMQYMYMyZKeq0/eexML8=
|
||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc/go.mod h1:kyq3g5w42zl/AKlbzDGppYpGMQYMYMyZKeq0/eexML8=
|
||||
istio.io/client-go v0.0.0-20200324231043-96a582576da1 h1:DTiU1Xcb38riKGTI6bL14lcCZgW2/HdqX8NXG2HByRQ=
|
||||
istio.io/client-go v0.0.0-20200324231043-96a582576da1/go.mod h1:nSSQnALPGh+QfuiQ09DpSCcgXolWEhRpmIqwqqptckw=
|
||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751 h1:yH62fTmV+5l1XVTWcomsc1jjH/oH9u/tTgn5NVmdIac=
|
||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751 h1:yH62fTmV+5l1XVTWcomsc1jjH/oH9u/tTgn5NVmdIac=
|
||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751/go.mod h1:4SGvmmus5HNFdqQsIL+uQO1PbAhjQKtSjMTqwsvYHlg=
|
||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751/go.mod h1:4SGvmmus5HNFdqQsIL+uQO1PbAhjQKtSjMTqwsvYHlg=
|
||||
istio.io/gogo-genproto v0.0.0-20190614210408-e88dc8b0e4db/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs=
|
||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A=
|
||||
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
|
||||
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
|
||||
k8s.io/api v0.18.1/go.mod h1:3My4jorQWzSs5a+l7Ge6JBbIxChLnY8HnuT58ZWolss=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
|
||||
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
|
||||
k8s.io/api v0.18.8 h1:aIKUzJPb96f3fKec2lxtY7acZC9gQNDLVhfSGpxBAC4=
|
||||
k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
|
||||
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
|
||||
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
|
||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA=
|
||||
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
|
||||
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
|
||||
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0=
|
||||
k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig=
|
||||
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
|
||||
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
|
||||
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
|
||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
|
||||
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
|
||||
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
|
||||
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
|
||||
k8s.io/client-go v0.17.1/go.mod h1:HZtHJSC/VuSHcETN9QA5QDZky1tXiYrkF/7t7vRpO1A=
|
||||
k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc=
|
||||
k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
|
||||
k8s.io/client-go v0.17.5 h1:Sm/9AQ415xPAX42JLKbJZnreXFgD2rVfDUDwOTm0gzA=
|
||||
k8s.io/client-go v0.17.5/go.mod h1:S8uZpBpjJJdEH/fEyxcqg7Rn0P5jH+ilkgBHjriSmNo=
|
||||
k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY=
|
||||
k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
|
||||
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
|
||||
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
|
||||
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
|
||||
k8s.io/code-generator v0.0.0-20190311093542-50b561225d70/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
|
||||
k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM=
|
||||
k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU=
|
||||
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269 h1:d8Fm55A+7HOczX58+x9x+nJnJ1Devt1aCrWVIPaw/Vg=
|
||||
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
|
||||
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
|
||||
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
|
||||
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
|
||||
k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d h1:jocF7XFucw2pEiv2wS7wk2FRFCjDFGV1oa4TMs0SAT0=
|
||||
k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
|
||||
@ -1037,7 +858,6 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 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 config
|
||||
|
||||
// FastPoll used for fast testing
|
||||
|
@ -1,12 +1,28 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"log"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/internal/config"
|
||||
)
|
||||
|
||||
|
@ -3,18 +3,12 @@ kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ['']
|
||||
resources: ['endpoints', 'pods', 'services']
|
||||
verbs: ['get', 'watch', 'list']
|
||||
- apiGroups: ['extensions']
|
||||
resources: ['ingresses']
|
||||
verbs: ['get', 'watch', 'list']
|
||||
- apiGroups: ['']
|
||||
resources: ['nodes']
|
||||
verbs: ['list']
|
||||
|
@ -7,6 +7,6 @@ roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
|
@ -15,9 +15,9 @@ spec:
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: us.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.7.2
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --registry=txt
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --registry=txt
|
||||
|
@ -1,5 +1,12 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
images:
|
||||
- name: k8s.gcr.io/external-dns/external-dns
|
||||
newTag: v0.7.4
|
||||
|
||||
resources:
|
||||
- ./external-dns-deployment.yaml
|
||||
- ./external-dns-serviceaccount.yaml
|
||||
- ./external-dns-clusterrole.yaml
|
||||
- ./external-dns-clusterrolebinding.yaml
|
||||
- ./external-dns-deployment.yaml
|
||||
- ./external-dns-serviceaccount.yaml
|
||||
- ./external-dns-clusterrole.yaml
|
||||
- ./external-dns-clusterrolebinding.yaml
|
||||
|
22
main.go
22
main.go
@ -26,8 +26,8 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"sigs.k8s.io/external-dns/controller"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
@ -58,6 +58,7 @@ import (
|
||||
"sigs.k8s.io/external-dns/provider/rcode0"
|
||||
"sigs.k8s.io/external-dns/provider/rdns"
|
||||
"sigs.k8s.io/external-dns/provider/rfc2136"
|
||||
"sigs.k8s.io/external-dns/provider/scaleway"
|
||||
"sigs.k8s.io/external-dns/provider/transip"
|
||||
"sigs.k8s.io/external-dns/provider/ultradns"
|
||||
"sigs.k8s.io/external-dns/provider/vinyldns"
|
||||
@ -99,9 +100,11 @@ func main() {
|
||||
sourceCfg := &source.Config{
|
||||
Namespace: cfg.Namespace,
|
||||
AnnotationFilter: cfg.AnnotationFilter,
|
||||
LabelFilter: cfg.LabelFilter,
|
||||
FQDNTemplate: cfg.FQDNTemplate,
|
||||
CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
|
||||
IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation,
|
||||
IgnoreIngressTLSSpec: cfg.IgnoreIngressTLSSpec,
|
||||
Compatibility: cfg.Compatibility,
|
||||
PublishInternal: cfg.PublishInternal,
|
||||
PublishHostIP: cfg.PublishHostIP,
|
||||
@ -146,6 +149,7 @@ func main() {
|
||||
} else {
|
||||
domainFilter = endpoint.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains)
|
||||
}
|
||||
zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter)
|
||||
zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
|
||||
zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
|
||||
zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
|
||||
@ -180,6 +184,7 @@ func main() {
|
||||
APIRetries: cfg.AWSAPIRetries,
|
||||
PreferCNAME: cfg.AWSPreferCNAME,
|
||||
DryRun: cfg.DryRun,
|
||||
ZoneCacheDuration: cfg.AWSZoneCacheDuration,
|
||||
},
|
||||
)
|
||||
case "aws-sd":
|
||||
@ -190,7 +195,7 @@ func main() {
|
||||
}
|
||||
p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun)
|
||||
case "azure-dns", "azure":
|
||||
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||
case "azure-private-dns":
|
||||
p, err = azure.NewAzurePrivateDNSProvider(domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureSubscriptionID, cfg.DryRun)
|
||||
case "vinyldns":
|
||||
@ -286,15 +291,18 @@ func main() {
|
||||
case "ns1":
|
||||
p, err = ns1.NewNS1Provider(
|
||||
ns1.NS1Config{
|
||||
DomainFilter: domainFilter,
|
||||
ZoneIDFilter: zoneIDFilter,
|
||||
NS1Endpoint: cfg.NS1Endpoint,
|
||||
NS1IgnoreSSL: cfg.NS1IgnoreSSL,
|
||||
DryRun: cfg.DryRun,
|
||||
DomainFilter: domainFilter,
|
||||
ZoneIDFilter: zoneIDFilter,
|
||||
NS1Endpoint: cfg.NS1Endpoint,
|
||||
NS1IgnoreSSL: cfg.NS1IgnoreSSL,
|
||||
DryRun: cfg.DryRun,
|
||||
MinTTLSeconds: cfg.NS1MinTTLSeconds,
|
||||
},
|
||||
)
|
||||
case "transip":
|
||||
p, err = transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun)
|
||||
case "scaleway":
|
||||
p, err = scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun)
|
||||
default:
|
||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/alecthomas/kingpin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/source"
|
||||
)
|
||||
|
||||
@ -41,15 +42,16 @@ type Config struct {
|
||||
APIServerURL string
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
IstioIngressGatewayServices []string
|
||||
ContourLoadBalancerService string
|
||||
SkipperRouteGroupVersion string
|
||||
Sources []string
|
||||
Namespace string
|
||||
AnnotationFilter string
|
||||
LabelFilter string
|
||||
FQDNTemplate string
|
||||
CombineFQDNAndAnnotation bool
|
||||
IgnoreHostnameAnnotation bool
|
||||
IgnoreIngressTLSSpec bool
|
||||
Compatibility string
|
||||
PublishInternal bool
|
||||
PublishHostIP bool
|
||||
@ -63,6 +65,7 @@ type Config struct {
|
||||
ExcludeDomains []string
|
||||
RegexDomainFilter string
|
||||
RegexDomainExclusion string
|
||||
ZoneNameFilter []string
|
||||
ZoneIDFilter []string
|
||||
AlibabaCloudConfigFile string
|
||||
AlibabaCloudZoneType string
|
||||
@ -74,6 +77,7 @@ type Config struct {
|
||||
AWSEvaluateTargetHealth bool
|
||||
AWSAPIRetries int
|
||||
AWSPreferCNAME bool
|
||||
AWSZoneCacheDuration time.Duration
|
||||
AzureConfigFile string
|
||||
AzureResourceGroup string
|
||||
AzureSubscriptionID string
|
||||
@ -141,6 +145,7 @@ type Config struct {
|
||||
RFC2136MinTTL time.Duration
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
NS1MinTTLSeconds int
|
||||
TransIPAccountName string
|
||||
TransIPPrivateKeyFile string
|
||||
DigitalOceanAPIPageSize int
|
||||
@ -155,9 +160,11 @@ var defaultConfig = &Config{
|
||||
Sources: nil,
|
||||
Namespace: "",
|
||||
AnnotationFilter: "",
|
||||
LabelFilter: "",
|
||||
FQDNTemplate: "",
|
||||
CombineFQDNAndAnnotation: false,
|
||||
IgnoreHostnameAnnotation: false,
|
||||
IgnoreIngressTLSSpec: false,
|
||||
Compatibility: "",
|
||||
PublishInternal: false,
|
||||
PublishHostIP: false,
|
||||
@ -179,6 +186,7 @@ var defaultConfig = &Config{
|
||||
AWSEvaluateTargetHealth: true,
|
||||
AWSAPIRetries: 3,
|
||||
AWSPreferCNAME: false,
|
||||
AWSZoneCacheDuration: 0 * time.Second,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
@ -304,13 +312,15 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
|
||||
|
||||
// Flags related to processing sources
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, crd, empty, skipper-routegroup,openshift-route)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route")
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, crd, empty, skipper-routegroup,openshift-route)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route")
|
||||
|
||||
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
|
||||
app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
|
||||
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
||||
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
|
||||
app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation)
|
||||
app.Flag("ignore-hostname-annotation", "Ignore hostname annotation when generating DNS names, valid only when using fqdn-template is set (optional, default: false)").BoolVar(&cfg.IgnoreHostnameAnnotation)
|
||||
app.Flag("ignore-ingress-tls-spec", "Ignore tls spec section in ingresses resources, applicable only for ingress sources (optional, default: false)").BoolVar(&cfg.IgnoreIngressTLSSpec)
|
||||
app.Flag("compatibility", "Process annotation semantics from legacy implementations (optional, options: mate, molecule)").Default(defaultConfig.Compatibility).EnumVar(&cfg.Compatibility, "", "mate", "molecule")
|
||||
app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal)
|
||||
app.Flag("publish-host-ip", "Allow external-dns to publish host-ip for headless services (optional)").BoolVar(&cfg.PublishHostIP)
|
||||
@ -321,11 +331,15 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
|
||||
|
||||
// Flags related to providers
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "vultr", "ultradns")
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns")
|
||||
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
|
||||
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
|
||||
<<<<<<< HEAD
|
||||
app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default("").StringVar(&cfg.RegexDomainFilter)
|
||||
app.Flag("regex-domain-exclusion", "Regex filter that excludes domains and target zones matched by regex-domain-filter (optional)").Default("").StringVar(&cfg.RegexDomainExclusion)
|
||||
=======
|
||||
app.Flag("zone-name-filter", "Filter target zones by zone domain (For now, only AzureDNS provider is using this flag); specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneNameFilter)
|
||||
>>>>>>> upstream/master
|
||||
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
|
||||
app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
|
||||
app.Flag("google-batch-change-size", "When using the Google provider, set the maximum number of changes that will be applied in each batch.").Default(strconv.Itoa(defaultConfig.GoogleBatchChangeSize)).IntVar(&cfg.GoogleBatchChangeSize)
|
||||
@ -340,6 +354,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("aws-evaluate-target-health", "When using the AWS provider, set whether to evaluate the health of a DNS target (default: enabled, disable with --no-aws-evaluate-target-health)").Default(strconv.FormatBool(defaultConfig.AWSEvaluateTargetHealth)).BoolVar(&cfg.AWSEvaluateTargetHealth)
|
||||
app.Flag("aws-api-retries", "When using the AWS provider, set the maximum number of retries for API calls before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries)
|
||||
app.Flag("aws-prefer-cname", "When using the AWS provider, prefer using CNAME instead of ALIAS (default: disabled)").BoolVar(&cfg.AWSPreferCNAME)
|
||||
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
|
||||
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
|
||||
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
||||
app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
|
||||
@ -373,6 +388,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("pdns-tls-enabled", "When using the PowerDNS/PDNS provider, specify whether to use TLS (default: false, requires --tls-ca, optionally specify --tls-client-cert and --tls-client-cert-key)").Default(strconv.FormatBool(defaultConfig.PDNSTLSEnabled)).BoolVar(&cfg.PDNSTLSEnabled)
|
||||
app.Flag("ns1-endpoint", "When using the NS1 provider, specify the URL of the API endpoint to target (default: https://api.nsone.net/v1/)").Default(defaultConfig.NS1Endpoint).StringVar(&cfg.NS1Endpoint)
|
||||
app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL)
|
||||
app.Flag("ns1-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.NS1MinTTLSeconds)
|
||||
app.Flag("digitalocean-api-page-size", "Configure the page size used when querying the DigitalOcean API.").Default(strconv.Itoa(defaultConfig.DigitalOceanAPIPageSize)).IntVar(&cfg.DigitalOceanAPIPageSize)
|
||||
|
||||
// Flags related to TLS communication
|
||||
|
@ -29,23 +29,24 @@ import (
|
||||
|
||||
var (
|
||||
minimalConfig = &Config{
|
||||
APIServerURL: "",
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||
Sources: []string{"service"},
|
||||
Namespace: "",
|
||||
FQDNTemplate: "",
|
||||
Compatibility: "",
|
||||
Provider: "google",
|
||||
GoogleProject: "",
|
||||
APIServerURL: "",
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||
Sources: []string{"service"},
|
||||
Namespace: "",
|
||||
FQDNTemplate: "",
|
||||
Compatibility: "",
|
||||
Provider: "google",
|
||||
GoogleProject: "",
|
||||
GoogleBatchChangeSize: 1000,
|
||||
GoogleBatchChangeInterval: time.Second,
|
||||
DomainFilter: []string{""},
|
||||
ExcludeDomains: []string{""},
|
||||
RegexDomainFilter: "",
|
||||
RegexDomainExclusion: "",
|
||||
ZoneNameFilter: []string{""},
|
||||
ZoneIDFilter: []string{""},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "",
|
||||
@ -56,6 +57,7 @@ var (
|
||||
AWSEvaluateTargetHealth: true,
|
||||
AWSAPIRetries: 3,
|
||||
AWSPreferCNAME: false,
|
||||
AWSZoneCacheDuration: 0 * time.Second,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
@ -105,17 +107,18 @@ var (
|
||||
}
|
||||
|
||||
overriddenConfig = &Config{
|
||||
APIServerURL: "http://127.0.0.1:8080",
|
||||
KubeConfig: "/some/path",
|
||||
RequestTimeout: time.Second * 77,
|
||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||
SkipperRouteGroupVersion: "zalando.org/v2",
|
||||
Sources: []string{"service", "ingress", "connector"},
|
||||
Namespace: "namespace",
|
||||
IgnoreHostnameAnnotation: true,
|
||||
FQDNTemplate: "{{.Name}}.service.example.com",
|
||||
Compatibility: "mate",
|
||||
Provider: "google",
|
||||
APIServerURL: "http://127.0.0.1:8080",
|
||||
KubeConfig: "/some/path",
|
||||
RequestTimeout: time.Second * 77,
|
||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||
SkipperRouteGroupVersion: "zalando.org/v2",
|
||||
Sources: []string{"service", "ingress", "connector"},
|
||||
Namespace: "namespace",
|
||||
IgnoreHostnameAnnotation: true,
|
||||
IgnoreIngressTLSSpec: true,
|
||||
FQDNTemplate: "{{.Name}}.service.example.com",
|
||||
Compatibility: "mate",
|
||||
Provider: "google",
|
||||
GoogleProject: "project",
|
||||
GoogleBatchChangeSize: 100,
|
||||
GoogleBatchChangeInterval: time.Second * 2,
|
||||
@ -123,6 +126,7 @@ var (
|
||||
ExcludeDomains: []string{"xapi.example.org", "xapi.company.com"},
|
||||
RegexDomainFilter: "(example\\.org|company\\.com)$",
|
||||
RegexDomainExclusion: "xapi\\.(example\\.org|company\\.com)$",
|
||||
ZoneNameFilter: []string{"yapi.example.org", "yapi.company.com"},
|
||||
ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "private",
|
||||
@ -133,6 +137,7 @@ var (
|
||||
AWSEvaluateTargetHealth: false,
|
||||
AWSAPIRetries: 13,
|
||||
AWSPreferCNAME: true,
|
||||
AWSZoneCacheDuration: 10 * time.Second,
|
||||
AzureConfigFile: "azure.json",
|
||||
AzureResourceGroup: "arg",
|
||||
AzureSubscriptionID: "arg",
|
||||
@ -218,6 +223,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"--namespace=namespace",
|
||||
"--fqdn-template={{.Name}}.service.example.com",
|
||||
"--ignore-hostname-annotation",
|
||||
"--ignore-ingress-tls-spec",
|
||||
"--compatibility=mate",
|
||||
"--provider=google",
|
||||
"--google-project=project",
|
||||
@ -258,6 +264,8 @@ func TestParseFlags(t *testing.T) {
|
||||
"--exclude-domains=xapi.company.com",
|
||||
"--regex-domain-filter=(example\\.org|company\\.com)$",
|
||||
"--regex-domain-exclusion=xapi\\.(example\\.org|company\\.com)$",
|
||||
"--zone-name-filter=yapi.example.org",
|
||||
"--zone-name-filter=yapi.company.com",
|
||||
"--zone-id-filter=/hostedzone/ZTST1",
|
||||
"--zone-id-filter=/hostedzone/ZTST2",
|
||||
"--aws-zone-type=private",
|
||||
@ -267,6 +275,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"--aws-batch-change-interval=2s",
|
||||
"--aws-api-retries=13",
|
||||
"--aws-prefer-cname",
|
||||
"--aws-zones-cache-duration=10s",
|
||||
"--no-aws-evaluate-target-health",
|
||||
"--policy=upsert-only",
|
||||
"--registry=noop",
|
||||
@ -309,6 +318,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
||||
"EXTERNAL_DNS_FQDN_TEMPLATE": "{{.Name}}.service.example.com",
|
||||
"EXTERNAL_DNS_IGNORE_HOSTNAME_ANNOTATION": "1",
|
||||
"EXTERNAL_DNS_IGNORE_INGRESS_TLS_SPEC": "1",
|
||||
"EXTERNAL_DNS_COMPATIBILITY": "mate",
|
||||
"EXTERNAL_DNS_PROVIDER": "google",
|
||||
"EXTERNAL_DNS_GOOGLE_PROJECT": "project",
|
||||
@ -347,6 +357,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_TLS_CA": "/path/to/ca.crt",
|
||||
"EXTERNAL_DNS_TLS_CLIENT_CERT": "/path/to/cert.pem",
|
||||
"EXTERNAL_DNS_TLS_CLIENT_CERT_KEY": "/path/to/key.pem",
|
||||
"EXTERNAL_DNS_ZONE_NAME_FILTER": "yapi.example.org\nyapi.company.com",
|
||||
"EXTERNAL_DNS_ZONE_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TYPE": "private",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TAGS": "tag=foo",
|
||||
@ -356,6 +367,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_AWS_EVALUATE_TARGET_HEALTH": "0",
|
||||
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
|
||||
"EXTERNAL_DNS_AWS_PREFER_CNAME": "true",
|
||||
"EXTERNAL_DNS_AWS_ZONES_CACHE_DURATION": "10s",
|
||||
"EXTERNAL_DNS_POLICY": "upsert-only",
|
||||
"EXTERNAL_DNS_REGISTRY": "noop",
|
||||
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
|
||||
|
@ -242,7 +242,7 @@ func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.Do
|
||||
// Explicitly specify which records we want to use for planning.
|
||||
// TODO: Add AAAA records as well when they are supported.
|
||||
switch record.RecordType {
|
||||
case endpoint.RecordTypeA, endpoint.RecordTypeCNAME:
|
||||
case endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS:
|
||||
filtered = append(filtered, record)
|
||||
default:
|
||||
continue
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
c "github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
|
@ -326,10 +326,6 @@ func (p *AlibabaCloudProvider) recordsForDNS() (endpoints []*endpoint.Endpoint,
|
||||
recordType := recordList[0].Type
|
||||
ttl := recordList[0].TTL
|
||||
|
||||
if ttl == defaultAlibabaCloudRecordTTL {
|
||||
ttl = 0
|
||||
}
|
||||
|
||||
var targets []string
|
||||
for _, record := range recordList {
|
||||
target := record.Value
|
||||
|
@ -276,6 +276,12 @@ func TestAlibabaCloudProvider_Records(t *testing.T) {
|
||||
|
||||
func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
|
||||
p := newTestAlibabaCloudProvider(false)
|
||||
defaultTtlPlan := &endpoint.Endpoint{
|
||||
DNSName: "ttl.container-service.top",
|
||||
RecordType: "A",
|
||||
RecordTTL: defaultAlibabaCloudRecordTTL,
|
||||
Targets: endpoint.NewTargets("4.3.2.1"),
|
||||
}
|
||||
changes := plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
@ -284,6 +290,7 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
|
||||
RecordTTL: 300,
|
||||
Targets: endpoint.NewTargets("4.3.2.1"),
|
||||
},
|
||||
defaultTtlPlan,
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
{
|
||||
@ -308,13 +315,20 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get records: %v", err)
|
||||
} else {
|
||||
if len(endpoints) != 2 {
|
||||
if len(endpoints) != 3 {
|
||||
t.Errorf("Incorrect number of records: %d", len(endpoints))
|
||||
}
|
||||
for _, endpoint := range endpoints {
|
||||
t.Logf("Endpoint for %++v", *endpoint)
|
||||
}
|
||||
}
|
||||
for _, ep := range endpoints {
|
||||
if ep.DNSName == defaultTtlPlan.DNSName {
|
||||
if ep.RecordTTL != defaultTtlPlan.RecordTTL {
|
||||
t.Error("default ttl execute error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlibabaCloudProvider_Records_PrivateZone(t *testing.T) {
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/route53"
|
||||
"github.com/linki/instrumented_http"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
@ -49,6 +50,7 @@ const (
|
||||
providerSpecificGeolocationCountryCode = "aws/geolocation-country-code"
|
||||
providerSpecificGeolocationSubdivisionCode = "aws/geolocation-subdivision-code"
|
||||
providerSpecificMultiValueAnswer = "aws/multi-value-answer"
|
||||
providerSpecificHealthCheckID = "aws/health-check-id"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -78,6 +80,7 @@ var (
|
||||
"us-gov-west-1.elb.amazonaws.com": "Z33AYJ8TM3BH4J",
|
||||
"us-gov-east-1.elb.amazonaws.com": "Z166TLBEWOO7G0",
|
||||
"me-south-1.elb.amazonaws.com": "ZS929ML54UICD",
|
||||
"af-south-1.elb.amazonaws.com": "Z268VQBMOI5EKX",
|
||||
// Network Load Balancers
|
||||
"elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP",
|
||||
"elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI",
|
||||
@ -101,6 +104,9 @@ var (
|
||||
"elb.us-gov-west-1.amazonaws.com": "ZMG1MZ2THAWF1",
|
||||
"elb.us-gov-east-1.amazonaws.com": "Z1ZSMQQ6Q24QQ8",
|
||||
"elb.me-south-1.amazonaws.com": "Z3QSRYVP46NYYV",
|
||||
"elb.af-south-1.amazonaws.com": "Z203XCE67M25HM",
|
||||
// Global Accelerator
|
||||
"awsglobalaccelerator.com": "Z2BJ6XQ5FK7U4H",
|
||||
}
|
||||
)
|
||||
|
||||
@ -114,6 +120,12 @@ type Route53API interface {
|
||||
ListTagsForResourceWithContext(ctx context.Context, input *route53.ListTagsForResourceInput, opts ...request.Option) (*route53.ListTagsForResourceOutput, error)
|
||||
}
|
||||
|
||||
type zonesListCache struct {
|
||||
age time.Time
|
||||
duration time.Duration
|
||||
zones map[string]*route53.HostedZone
|
||||
}
|
||||
|
||||
// AWSProvider is an implementation of Provider for AWS Route53.
|
||||
type AWSProvider struct {
|
||||
provider.BaseProvider
|
||||
@ -131,6 +143,7 @@ type AWSProvider struct {
|
||||
// filter hosted zones by tags
|
||||
zoneTagFilter provider.ZoneTagFilter
|
||||
preferCNAME bool
|
||||
zonesCache *zonesListCache
|
||||
}
|
||||
|
||||
// AWSConfig contains configuration to create a new AWS provider.
|
||||
@ -146,6 +159,7 @@ type AWSConfig struct {
|
||||
APIRetries int
|
||||
PreferCNAME bool
|
||||
DryRun bool
|
||||
ZoneCacheDuration time.Duration
|
||||
}
|
||||
|
||||
// NewAWSProvider initializes a new AWS Route53 based Provider.
|
||||
@ -166,7 +180,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to instantiate AWS session")
|
||||
}
|
||||
|
||||
if awsConfig.AssumeRole != "" {
|
||||
@ -185,6 +199,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
|
||||
evaluateTargetHealth: awsConfig.EvaluateTargetHealth,
|
||||
preferCNAME: awsConfig.PreferCNAME,
|
||||
dryRun: awsConfig.DryRun,
|
||||
zonesCache: &zonesListCache{duration: awsConfig.ZoneCacheDuration},
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
@ -192,6 +207,12 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
|
||||
|
||||
// Zones returns the list of hosted zones.
|
||||
func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53.HostedZone, error) {
|
||||
if p.zonesCache.zones != nil && time.Since(p.zonesCache.age) < p.zonesCache.duration {
|
||||
log.Debug("Using cached zones list")
|
||||
return p.zonesCache.zones, nil
|
||||
}
|
||||
log.Debug("Refreshing zones list cache")
|
||||
|
||||
zones := make(map[string]*route53.HostedZone)
|
||||
|
||||
var tagErr error
|
||||
@ -229,16 +250,21 @@ func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53.HostedZone
|
||||
|
||||
err := p.client.ListHostedZonesPagesWithContext(ctx, &route53.ListHostedZonesInput{}, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to list hosted zones")
|
||||
}
|
||||
if tagErr != nil {
|
||||
return nil, tagErr
|
||||
return nil, errors.Wrap(tagErr, "failed to list zones tags")
|
||||
}
|
||||
|
||||
for _, zone := range zones {
|
||||
log.Debugf("Considering zone: %s (domain: %s)", aws.StringValue(zone.Id), aws.StringValue(zone.Name))
|
||||
}
|
||||
|
||||
if p.zonesCache.duration > time.Duration(0) {
|
||||
p.zonesCache.zones = zones
|
||||
p.zonesCache.age = time.Now()
|
||||
}
|
||||
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
@ -255,7 +281,7 @@ func wildcardUnescape(s string) string {
|
||||
func (p *AWSProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, _ error) {
|
||||
zones, err := p.Zones(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "records retrieval failed")
|
||||
}
|
||||
|
||||
return p.records(ctx, zones)
|
||||
@ -326,6 +352,11 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*route53.Hos
|
||||
// one of the above needs to be set, otherwise SetIdentifier doesn't make sense
|
||||
}
|
||||
}
|
||||
|
||||
if r.HealthCheckId != nil {
|
||||
ep.WithProviderSpecific(providerSpecificHealthCheckID, aws.StringValue(r.HealthCheckId))
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, ep)
|
||||
}
|
||||
}
|
||||
@ -339,7 +370,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*route53.Hos
|
||||
}
|
||||
|
||||
if err := p.client.ListResourceRecordSetsPagesWithContext(ctx, params, f); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to list resource records sets for zone %s", *z.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,12 +395,12 @@ func (p *AWSProvider) DeleteRecords(ctx context.Context, endpoints []*endpoint.E
|
||||
func (p *AWSProvider) doRecords(ctx context.Context, action string, endpoints []*endpoint.Endpoint) error {
|
||||
zones, err := p.Zones(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "failed to list zones, aborting %s doRecords action", action)
|
||||
}
|
||||
|
||||
records, err := p.records(ctx, zones)
|
||||
if err != nil {
|
||||
log.Errorf("getting records failed: %v", err)
|
||||
log.Errorf("failed to list records while preparing %s doRecords action: %s", action, err)
|
||||
}
|
||||
return p.submitChanges(ctx, p.newChanges(action, endpoints, records, zones), zones)
|
||||
}
|
||||
@ -378,7 +409,7 @@ func (p *AWSProvider) doRecords(ctx context.Context, action string, endpoints []
|
||||
func (p *AWSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
zones, err := p.Zones(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to list zones, not applying changes")
|
||||
}
|
||||
|
||||
records, ok := ctx.Value(provider.RecordsContextKey).([]*endpoint.Endpoint)
|
||||
@ -386,7 +417,7 @@ func (p *AWSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) e
|
||||
var err error
|
||||
records, err = p.records(ctx, zones)
|
||||
if err != nil {
|
||||
log.Errorf("getting records failed: %v", err)
|
||||
log.Errorf("failed to get records while preparing to applying changes: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,7 +484,7 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes []*route53.Chan
|
||||
}
|
||||
|
||||
if len(failedZones) > 0 {
|
||||
return fmt.Errorf("failed to submit all changes for the following zones: %v", failedZones)
|
||||
return errors.Errorf("failed to submit all changes for the following zones: %v", failedZones)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -572,6 +603,10 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint, recordsCac
|
||||
}
|
||||
}
|
||||
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificHealthCheckID); ok {
|
||||
change.ResourceRecordSet.HealthCheckId = aws.String(prop.Value)
|
||||
}
|
||||
|
||||
return change, dualstack
|
||||
}
|
||||
|
||||
@ -581,7 +616,7 @@ func (p *AWSProvider) tagsForZone(ctx context.Context, zoneID string) (map[strin
|
||||
ResourceId: aws.String(zoneID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to list tags for zone %s", zoneID)
|
||||
}
|
||||
tagMap := map[string]string{}
|
||||
for _, tag := range response.ResourceTagSet.Tags {
|
||||
|
@ -326,6 +326,8 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
endpoint.NewEndpoint("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"),
|
||||
})
|
||||
|
||||
records, err := provider.Records(context.Background())
|
||||
@ -347,6 +349,8 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -500,6 +504,7 @@ func TestAWSApplyChanges(t *testing.T) {
|
||||
|
||||
ctx := tt.setup(provider)
|
||||
|
||||
provider.zonesCache = &zonesListCache{duration: 0 * time.Minute}
|
||||
counter := NewRoute53APICounter(provider.client)
|
||||
provider.client = counter
|
||||
require.NoError(t, provider.ApplyChanges(ctx, changes))
|
||||
@ -1026,6 +1031,7 @@ func TestAWSCanonicalHostedZone(t *testing.T) {
|
||||
{"foo.sa-east-1.elb.amazonaws.com", "Z2P70J7HTTTPLU"},
|
||||
{"foo.cn-north-1.elb.amazonaws.com.cn", "Z1GDH35T77C1KE"},
|
||||
{"foo.cn-northwest-1.elb.amazonaws.com.cn", "ZM7IZAIOVVDZF"},
|
||||
{"foo.af-south-1.elb.amazonaws.com", "Z268VQBMOI5EKX"},
|
||||
// Network Load Balancers
|
||||
{"foo.elb.us-east-2.amazonaws.com", "ZLMOA37VPKANP"},
|
||||
{"foo.elb.us-east-1.amazonaws.com", "Z26RNL4JYFTOTI"},
|
||||
@ -1045,6 +1051,7 @@ func TestAWSCanonicalHostedZone(t *testing.T) {
|
||||
{"foo.elb.sa-east-1.amazonaws.com", "ZTK26PT1VY4CU"},
|
||||
{"foo.elb.cn-north-1.amazonaws.com.cn", "Z3QFB96KMJ7ED6"},
|
||||
{"foo.elb.cn-northwest-1.amazonaws.com.cn", "ZQEIKTCZ8352D"},
|
||||
{"foo.elb.af-south-1.amazonaws.com", "Z203XCE67M25HM"},
|
||||
// No Load Balancer
|
||||
{"foo.example.org", ""},
|
||||
} {
|
||||
@ -1200,6 +1207,7 @@ func newAWSProviderWithTagFilter(t *testing.T, domainFilter endpoint.DomainFilte
|
||||
zoneTypeFilter: zoneTypeFilter,
|
||||
zoneTagFilter: zoneTagFilter,
|
||||
dryRun: false,
|
||||
zonesCache: &zonesListCache{duration: 1 * time.Minute},
|
||||
}
|
||||
|
||||
createAWSZone(t, provider, &route53.HostedZone{
|
||||
|
@ -69,6 +69,7 @@ type RecordSetsClient interface {
|
||||
type AzureProvider struct {
|
||||
provider.BaseProvider
|
||||
domainFilter endpoint.DomainFilter
|
||||
zoneNameFilter endpoint.DomainFilter
|
||||
zoneIDFilter provider.ZoneIDFilter
|
||||
dryRun bool
|
||||
resourceGroup string
|
||||
@ -80,7 +81,7 @@ type AzureProvider struct {
|
||||
// NewAzureProvider creates a new Azure provider.
|
||||
//
|
||||
// Returns the provider or an error if a provider could not be created.
|
||||
func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, resourceGroup string, userAssignedIdentityClientID string, dryRun bool) (*AzureProvider, error) {
|
||||
func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, resourceGroup string, userAssignedIdentityClientID string, dryRun bool) (*AzureProvider, error) {
|
||||
contents, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
|
||||
@ -122,6 +123,7 @@ func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zon
|
||||
|
||||
provider := &AzureProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneNameFilter: zoneNameFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: cfg.ResourceGroup,
|
||||
@ -205,6 +207,11 @@ func (p *AzureProvider) Records(ctx context.Context) (endpoints []*endpoint.Endp
|
||||
return true
|
||||
}
|
||||
name := formatAzureDNSName(*recordSet.Name, *zone.Name)
|
||||
|
||||
if len(p.zoneNameFilter.Filters) > 0 && !p.domainFilter.Match(name) {
|
||||
log.Debugf("Skipping return of record %s because it was filtered out by the specified --domain-filter", name)
|
||||
return true
|
||||
}
|
||||
targets := extractAzureTargets(&recordSet)
|
||||
if len(targets) == 0 {
|
||||
log.Errorf("Failed to extract targets for '%s' with type '%s'.", name, recordType)
|
||||
@ -262,6 +269,9 @@ func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) {
|
||||
|
||||
if zone.Name != nil && p.domainFilter.Match(*zone.Name) && p.zoneIDFilter.Match(*zone.ID) {
|
||||
zones = append(zones, zone)
|
||||
} else if zone.Name != nil && len(p.zoneNameFilter.Filters) > 0 && p.zoneNameFilter.Match(*zone.Name) {
|
||||
// Handle zoneNameFilter
|
||||
zones = append(zones, zone)
|
||||
}
|
||||
|
||||
err := zonesIterator.NextWithContext(ctx)
|
||||
@ -344,6 +354,10 @@ func (p *AzureProvider) deleteRecords(ctx context.Context, deleted azureChangeMa
|
||||
for zone, endpoints := range deleted {
|
||||
for _, endpoint := range endpoints {
|
||||
name := p.recordSetNameForZone(zone, endpoint)
|
||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
||||
log.Debugf("Skipping deletion of record %s because it was filtered out by the specified --domain-filter", endpoint.DNSName)
|
||||
continue
|
||||
}
|
||||
if p.dryRun {
|
||||
log.Infof("Would delete %s record named '%s' for Azure DNS zone '%s'.", endpoint.RecordType, name, zone)
|
||||
} else {
|
||||
@ -366,6 +380,10 @@ func (p *AzureProvider) updateRecords(ctx context.Context, updated azureChangeMa
|
||||
for zone, endpoints := range updated {
|
||||
for _, endpoint := range endpoints {
|
||||
name := p.recordSetNameForZone(zone, endpoint)
|
||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
||||
log.Debugf("Skipping update of record %s because it was filtered out by the specified --domain-filter", endpoint.DNSName)
|
||||
continue
|
||||
}
|
||||
if p.dryRun {
|
||||
log.Infof(
|
||||
"Would update %s record named '%s' to '%s' for Azure DNS zone '%s'.",
|
||||
|
@ -65,9 +65,14 @@ func NewAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneIDFilter
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zonesClient := privatedns.NewPrivateZonesClient(subscriptionID)
|
||||
settings, err := auth.GetSettingsFromEnvironment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zonesClient := privatedns.NewPrivateZonesClientWithBaseURI(settings.Environment.ResourceManagerEndpoint, subscriptionID)
|
||||
zonesClient.Authorizer = authorizer
|
||||
recordSetsClient := privatedns.NewRecordSetsClient(subscriptionID)
|
||||
recordSetsClient := privatedns.NewRecordSetsClientWithBaseURI(settings.Environment.ResourceManagerEndpoint, subscriptionID)
|
||||
recordSetsClient.Authorizer = authorizer
|
||||
|
||||
provider := &AzurePrivateDNSProvider{
|
||||
|
@ -18,12 +18,15 @@ package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
@ -252,6 +255,36 @@ func newAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneIDFilter
|
||||
}
|
||||
}
|
||||
|
||||
func validateAzurePrivateDNSClientsResourceManager(t *testing.T, environmentName string, expectedResourceManagerEndpoint string) {
|
||||
err := os.Setenv(auth.EnvironmentName, environmentName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
azurePrivateDNSProvider, err := NewAzurePrivateDNSProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), "k8s", "sub", true)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
zonesClientBaseURI := azurePrivateDNSProvider.zonesClient.(privatedns.PrivateZonesClient).BaseURI
|
||||
recordSetsClientBaseURI := azurePrivateDNSProvider.recordSetsClient.(privatedns.RecordSetsClient).BaseURI
|
||||
|
||||
assert.Equal(t, zonesClientBaseURI, expectedResourceManagerEndpoint, "expected and actual resource manager endpoints don't match. expected: %s, got: %s", expectedResourceManagerEndpoint, zonesClientBaseURI)
|
||||
assert.Equal(t, recordSetsClientBaseURI, expectedResourceManagerEndpoint, "expected and actual resource manager endpoints don't match. expected: %s, got: %s", expectedResourceManagerEndpoint, recordSetsClientBaseURI)
|
||||
}
|
||||
|
||||
func TestNewAzurePrivateDNSProvider(t *testing.T) {
|
||||
// make sure to reset the environment variables at the end again
|
||||
originalEnv := os.Getenv(auth.EnvironmentName)
|
||||
defer os.Setenv(auth.EnvironmentName, originalEnv)
|
||||
|
||||
validateAzurePrivateDNSClientsResourceManager(t, "", azure.PublicCloud.ResourceManagerEndpoint)
|
||||
validateAzurePrivateDNSClientsResourceManager(t, "AZURECHINACLOUD", azure.ChinaCloud.ResourceManagerEndpoint)
|
||||
validateAzurePrivateDNSClientsResourceManager(t, "AZUREGERMANCLOUD", azure.GermanCloud.ResourceManagerEndpoint)
|
||||
validateAzurePrivateDNSClientsResourceManager(t, "AZUREUSGOVERNMENTCLOUD", azure.USGovernmentCloud.ResourceManagerEndpoint)
|
||||
}
|
||||
|
||||
func TestAzurePrivateDNSRecord(t *testing.T) {
|
||||
provider, err := newMockedAzurePrivateDNSProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s",
|
||||
&[]privatedns.PrivateZone{
|
||||
|
@ -207,7 +207,7 @@ func (client *mockRecordSetsClient) CreateOrUpdate(ctx context.Context, resource
|
||||
}
|
||||
|
||||
// newMockedAzureProvider creates an AzureProvider comprising the mocked clients for zones and recordsets
|
||||
func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zones *[]dns.Zone, recordSets *[]dns.RecordSet) (*AzureProvider, error) {
|
||||
func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zones *[]dns.Zone, recordSets *[]dns.RecordSet) (*AzureProvider, error) {
|
||||
// init zone-related parts of the mock-client
|
||||
pageIterator := mockZoneListResultPageIterator{
|
||||
results: []dns.ZoneListResult{
|
||||
@ -237,12 +237,13 @@ func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneIDFilter pro
|
||||
mockRecordSetListIterator: &mockRecordSetListIterator,
|
||||
}
|
||||
|
||||
return newAzureProvider(domainFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, &zonesClient, &recordSetsClient), nil
|
||||
return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, &zonesClient, &recordSetsClient), nil
|
||||
}
|
||||
|
||||
func newAzureProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider {
|
||||
func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider {
|
||||
return &AzureProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneNameFilter: zoneNameFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: resourceGroup,
|
||||
@ -257,7 +258,7 @@ func validateAzureEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expect
|
||||
}
|
||||
|
||||
func TestAzureRecord(t *testing.T) {
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
@ -294,7 +295,7 @@ func TestAzureRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureMultiRecord(t *testing.T) {
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
@ -393,6 +394,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
|
||||
}
|
||||
|
||||
provider := newAzureProvider(
|
||||
endpoint.NewDomainFilter([]string{""}),
|
||||
endpoint.NewDomainFilter([]string{""}),
|
||||
provider.NewZoneIDFilter([]string{""}),
|
||||
dryRun,
|
||||
@ -496,3 +498,138 @@ func TestAzureGetAccessToken(t *testing.T) {
|
||||
t.Fatalf("expect the clientID of the token is SPNClientID, but got token %s", string(innerToken))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAzureNameFilter(t *testing.T) {
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"nginx.example.com"}), endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
|
||||
&[]dns.RecordSet{
|
||||
createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
|
||||
createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
|
||||
createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"),
|
||||
createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
|
||||
createMockRecordSetWithTTL("test.nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
actual, err := provider.Records(ctx)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("test.nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestAzureApplyChangesZoneName(t *testing.T) {
|
||||
recordsClient := mockRecordSetsClient{}
|
||||
|
||||
testAzureApplyChangesInternalZoneName(t, false, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.foo.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("oldcname.foo.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
})
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"),
|
||||
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
})
|
||||
}
|
||||
|
||||
func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client RecordSetsClient) {
|
||||
zlr := dns.ZoneListResult{
|
||||
Value: &[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
}
|
||||
|
||||
results := []dns.ZoneListResult{
|
||||
zlr,
|
||||
}
|
||||
|
||||
mockZoneListResultPage := dns.NewZoneListResultPage(func(ctxParam context.Context, zlrParam dns.ZoneListResult) (dns.ZoneListResult, error) {
|
||||
if len(results) > 0 {
|
||||
result := results[0]
|
||||
results = nil
|
||||
return result, nil
|
||||
}
|
||||
return dns.ZoneListResult{}, nil
|
||||
})
|
||||
mockZoneClientIterator := dns.NewZoneListResultIterator(mockZoneListResultPage)
|
||||
|
||||
zonesClient := mockZonesClient{
|
||||
mockZonesClientIterator: &mockZoneClientIterator,
|
||||
}
|
||||
|
||||
provider := newAzureProvider(
|
||||
endpoint.NewDomainFilter([]string{"foo.example.com"}),
|
||||
endpoint.NewDomainFilter([]string{"example.com"}),
|
||||
provider.NewZoneIDFilter([]string{""}),
|
||||
dryRun,
|
||||
"group",
|
||||
"",
|
||||
&zonesClient,
|
||||
client,
|
||||
)
|
||||
|
||||
createRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"),
|
||||
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
|
||||
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
|
||||
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.foo.example.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldcname.foo.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("old.nope.example.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpoint("new.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, "111.222.111.222"),
|
||||
endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("deleted.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"),
|
||||
}
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: createRecords,
|
||||
UpdateNew: updatedRecords,
|
||||
UpdateOld: currentRecords,
|
||||
Delete: deleteRecords,
|
||||
}
|
||||
|
||||
if err := provider.ApplyChanges(context.Background(), changes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -257,9 +257,9 @@ func AssertActions(t *testing.T, provider *CloudFlareProvider, endpoints []*endp
|
||||
|
||||
changes := plan.Calculate().Changes
|
||||
|
||||
// Records other than A and CNAME are not supported by planner, just create them
|
||||
// Records other than A, CNAME and NS are not supported by planner, just create them
|
||||
for _, endpoint := range endpoints {
|
||||
if endpoint.RecordType != "A" && endpoint.RecordType != "CNAME" {
|
||||
if endpoint.RecordType != "A" && endpoint.RecordType != "CNAME" && endpoint.RecordType != "NS" {
|
||||
changes.Create = append(changes.Create, endpoint)
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ func (c etcdClient) DeleteService(key string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// loads TLS artifacts and builds tls.Clonfig object
|
||||
// loads TLS artifacts and builds tls.Config object
|
||||
func newTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool) (*tls.Config, error) {
|
||||
if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" {
|
||||
return nil, errors.New("either both cert and key or none must be provided")
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
hclouddns "git.blindage.org/21h/hcloud-dns"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
@ -116,14 +117,40 @@ func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerC
|
||||
|
||||
for _, changes := range zoneChanges {
|
||||
for _, change := range changes {
|
||||
// Prepare record name
|
||||
recordName := strings.TrimSuffix(change.ResourceRecordSet.Name, "."+change.ZoneName)
|
||||
if recordName == change.ZoneName {
|
||||
recordName = "@"
|
||||
}
|
||||
if change.ResourceRecordSet.RecordType == hclouddns.CNAME && !strings.HasSuffix(change.ResourceRecordSet.Value, ".") {
|
||||
change.ResourceRecordSet.Value += "."
|
||||
}
|
||||
change.ResourceRecordSet.Name = recordName
|
||||
|
||||
// Get ID of record if not create operation
|
||||
if change.Action != hetznerCreate {
|
||||
allRecords, err := p.Client.GetRecords(hclouddns.HCloudGetRecordsParams{ZoneID: change.ZoneID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, record := range allRecords.Records {
|
||||
if record.Name == change.ResourceRecordSet.Name && record.RecordType == change.ResourceRecordSet.RecordType {
|
||||
change.ResourceRecordSet.ID = record.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"id": change.ResourceRecordSet.ID,
|
||||
"record": change.ResourceRecordSet.Name,
|
||||
"type": change.ResourceRecordSet.RecordType,
|
||||
"value": change.ResourceRecordSet.Value,
|
||||
"ttl": change.ResourceRecordSet.TTL,
|
||||
"action": change.Action,
|
||||
"zone": change.ZoneName,
|
||||
"zone_id": change.ZoneID,
|
||||
}).Info("Changing record.")
|
||||
}).Info("Changing record")
|
||||
|
||||
change.ResourceRecordSet.Name = strings.TrimSuffix(change.ResourceRecordSet.Name, "."+change.ZoneName)
|
||||
if change.ResourceRecordSet.Name == change.ZoneName {
|
||||
@ -142,13 +169,24 @@ func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerC
|
||||
Value: change.ResourceRecordSet.Value,
|
||||
TTL: change.ResourceRecordSet.TTL,
|
||||
}
|
||||
_, err := p.Client.CreateRecord(record)
|
||||
answer, err := p.Client.CreateRecord(record)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"Code": answer.Error.Code,
|
||||
"Message": answer.Error.Message,
|
||||
"Record name": answer.Record.Name,
|
||||
"Record type": answer.Record.RecordType,
|
||||
"Record value": answer.Record.Value,
|
||||
}).Warning("Create problem")
|
||||
return err
|
||||
}
|
||||
case hetznerDelete:
|
||||
_, err := p.Client.DeleteRecord(change.ResourceRecordSet.ID)
|
||||
answer, err := p.Client.DeleteRecord(change.ResourceRecordSet.ID)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"Code": answer.Error.Code,
|
||||
"Message": answer.Error.Message,
|
||||
}).Warning("Delete problem")
|
||||
return err
|
||||
}
|
||||
case hetznerUpdate:
|
||||
@ -158,9 +196,17 @@ func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerC
|
||||
Name: change.ResourceRecordSet.Name,
|
||||
Value: change.ResourceRecordSet.Value,
|
||||
TTL: change.ResourceRecordSet.TTL,
|
||||
ID: change.ResourceRecordSet.ID,
|
||||
}
|
||||
_, err := p.Client.UpdateRecord(record)
|
||||
answer, err := p.Client.UpdateRecord(record)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"Code": answer.Error.Code,
|
||||
"Message": answer.Error.Message,
|
||||
"Record name": answer.Record.Name,
|
||||
"Record type": answer.Record.RecordType,
|
||||
"Record value": answer.Record.Value,
|
||||
}).Warning("Update problem")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ type mockHCloudClientAdapter interface {
|
||||
ImportZoneString(zoneID string, zonePlainText string) (hclouddns.HCloudAnswerGetZone, error)
|
||||
ExportZoneToString(zoneID string) (hclouddns.HCloudAnswerGetZonePlainText, error)
|
||||
ValidateZoneString(zonePlainText string) (hclouddns.HCloudAnswerZoneValidate, error)
|
||||
GetRecord(ID string) (hclouddns.HCloudAnswerGetRecord, error)
|
||||
GetRecords(params hclouddns.HCloudGetRecordsParams) (hclouddns.HCloudAnswerGetRecords, error)
|
||||
UpdateRecord(record hclouddns.HCloudRecord) (hclouddns.HCloudAnswerGetRecord, error)
|
||||
DeleteRecord(ID string) (hclouddns.HCloudAnswerDeleteRecord, error)
|
||||
@ -95,6 +96,11 @@ func (m *mockHCloudClient) ValidateZoneString(zonePlainText string) (hclouddns.H
|
||||
}
|
||||
|
||||
// records
|
||||
|
||||
func (m *mockHCloudClient) GetRecord(ID string) (hclouddns.HCloudAnswerGetRecord, error) {
|
||||
return hclouddns.HCloudAnswerGetRecord{}, nil
|
||||
}
|
||||
|
||||
func (m *mockHCloudClient) GetRecords(params hclouddns.HCloudGetRecordsParams) (hclouddns.HCloudAnswerGetRecords, error) {
|
||||
return hclouddns.HCloudAnswerGetRecords{
|
||||
Records: []hclouddns.HCloudRecord{
|
||||
|
@ -232,7 +232,11 @@ func (p *InfobloxProvider) ApplyChanges(ctx context.Context, changes *plan.Chang
|
||||
|
||||
func (p *InfobloxProvider) zones() ([]ibclient.ZoneAuth, error) {
|
||||
var res, result []ibclient.ZoneAuth
|
||||
obj := ibclient.NewZoneAuth(ibclient.ZoneAuth{})
|
||||
obj := ibclient.NewZoneAuth(
|
||||
ibclient.ZoneAuth{
|
||||
View: p.view,
|
||||
},
|
||||
)
|
||||
err := p.client.GetObject(obj, "", &res)
|
||||
|
||||
if err != nil {
|
||||
|
@ -344,6 +344,7 @@ func TestInfobloxRecords(t *testing.T) {
|
||||
client := mockIBConnector{
|
||||
mockInfobloxZones: &[]ibclient.ZoneAuth{
|
||||
createMockInfobloxZone("example.com"),
|
||||
createMockInfobloxZone("other.com"),
|
||||
},
|
||||
mockInfobloxObjects: &[]ibclient.IBObject{
|
||||
createMockInfobloxObject("example.com", endpoint.RecordTypeA, "123.123.123.122"),
|
||||
|
@ -85,20 +85,22 @@ func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) {
|
||||
|
||||
// NS1Config passes cli args to the NS1Provider
|
||||
type NS1Config struct {
|
||||
DomainFilter endpoint.DomainFilter
|
||||
ZoneIDFilter provider.ZoneIDFilter
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
DryRun bool
|
||||
DomainFilter endpoint.DomainFilter
|
||||
ZoneIDFilter provider.ZoneIDFilter
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
DryRun bool
|
||||
MinTTLSeconds int
|
||||
}
|
||||
|
||||
// NS1Provider is the NS1 provider
|
||||
type NS1Provider struct {
|
||||
provider.BaseProvider
|
||||
client NS1DomainClient
|
||||
domainFilter endpoint.DomainFilter
|
||||
zoneIDFilter provider.ZoneIDFilter
|
||||
dryRun bool
|
||||
client NS1DomainClient
|
||||
domainFilter endpoint.DomainFilter
|
||||
zoneIDFilter provider.ZoneIDFilter
|
||||
dryRun bool
|
||||
minTTLSeconds int
|
||||
}
|
||||
|
||||
// NewNS1Provider creates a new NS1 Provider
|
||||
@ -135,9 +137,10 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr
|
||||
apiClient := api.NewClient(client, clientArgs...)
|
||||
|
||||
provider := &NS1Provider{
|
||||
client: NS1DomainService{apiClient},
|
||||
domainFilter: config.DomainFilter,
|
||||
zoneIDFilter: config.ZoneIDFilter,
|
||||
client: NS1DomainService{apiClient},
|
||||
domainFilter: config.DomainFilter,
|
||||
zoneIDFilter: config.ZoneIDFilter,
|
||||
minTTLSeconds: config.MinTTLSeconds,
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
@ -175,13 +178,16 @@ func (p *NS1Provider) Records(ctx context.Context) ([]*endpoint.Endpoint, error)
|
||||
}
|
||||
|
||||
// ns1BuildRecord returns a dns.Record for a change set
|
||||
func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record {
|
||||
func (p *NS1Provider) ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record {
|
||||
record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType)
|
||||
for _, v := range change.Endpoint.Targets {
|
||||
record.AddAnswer(dns.NewAnswer(strings.Split(v, " ")))
|
||||
}
|
||||
// set detault ttl
|
||||
// set default ttl, but respect minTTLSeconds
|
||||
var ttl = ns1DefaultTTL
|
||||
if p.minTTLSeconds > ttl {
|
||||
ttl = p.minTTLSeconds
|
||||
}
|
||||
if change.Endpoint.RecordTTL.IsConfigured() {
|
||||
ttl = int(change.Endpoint.RecordTTL)
|
||||
}
|
||||
@ -206,7 +212,7 @@ func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error {
|
||||
changesByZone := ns1ChangesByZone(zones, changes)
|
||||
for zoneName, changes := range changesByZone {
|
||||
for _, change := range changes {
|
||||
record := ns1BuildRecord(zoneName, change)
|
||||
record := p.ns1BuildRecord(zoneName, change)
|
||||
logFields := log.Fields{
|
||||
"record": record.Domain,
|
||||
"type": record.Type,
|
||||
|
@ -129,9 +129,10 @@ func (m *MockNS1ListZonesFail) ListZones() ([]*dns.Zone, *http.Response, error)
|
||||
|
||||
func TestNS1Records(t *testing.T) {
|
||||
provider := &NS1Provider{
|
||||
client: &MockNS1DomainClient{},
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"foo.com."}),
|
||||
zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
|
||||
client: &MockNS1DomainClient{},
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"foo.com."}),
|
||||
zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
|
||||
minTTLSeconds: 3600,
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
@ -195,10 +196,18 @@ func TestNS1BuildRecord(t *testing.T) {
|
||||
RecordType: "A",
|
||||
},
|
||||
}
|
||||
record := ns1BuildRecord("foo.com", change)
|
||||
|
||||
provider := &NS1Provider{
|
||||
client: &MockNS1DomainClient{},
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"foo.com."}),
|
||||
zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
|
||||
minTTLSeconds: 300,
|
||||
}
|
||||
|
||||
record := provider.ns1BuildRecord("foo.com", change)
|
||||
assert.Equal(t, "foo.com", record.Zone)
|
||||
assert.Equal(t, "new.foo.com", record.Domain)
|
||||
assert.Equal(t, ns1DefaultTTL, record.TTL)
|
||||
assert.Equal(t, 300, record.TTL)
|
||||
|
||||
changeWithTTL := &ns1Change{
|
||||
Action: ns1Create,
|
||||
@ -206,13 +215,13 @@ func TestNS1BuildRecord(t *testing.T) {
|
||||
DNSName: "new-b",
|
||||
Targets: endpoint.Targets{"target"},
|
||||
RecordType: "A",
|
||||
RecordTTL: 100,
|
||||
RecordTTL: 3600,
|
||||
},
|
||||
}
|
||||
record = ns1BuildRecord("foo.com", changeWithTTL)
|
||||
record = provider.ns1BuildRecord("foo.com", changeWithTTL)
|
||||
assert.Equal(t, "foo.com", record.Zone)
|
||||
assert.Equal(t, "new-b.foo.com", record.Domain)
|
||||
assert.Equal(t, 100, record.TTL)
|
||||
assert.Equal(t, 3600, record.TTL)
|
||||
}
|
||||
|
||||
func TestNS1ApplyChanges(t *testing.T) {
|
||||
|
@ -93,7 +93,7 @@ func TestOvhZoneRecords(t *testing.T) {
|
||||
zones, records, err := provider.zonesRecords(context.TODO())
|
||||
assert.NoError(err)
|
||||
assert.ElementsMatch(zones, []string{"example.org"})
|
||||
assert.ElementsMatch(records, []ovhRecord{{ID: 42, Zone: "example.org", ovhRecordFields: ovhRecordFields{SubDomain: "ovh", FieldType: "A", TTL: 10, Target: "203.0.113.42"}}})
|
||||
assert.ElementsMatch(records, []ovhRecord{{ID: 42, Zone: "example.org", ovhRecordFields: ovhRecordFields{SubDomain: "ovh", FieldType: "A", TTL: 10, Target: "203.0.113.42"}}, {ID: 24, Zone: "example.org", ovhRecordFields: ovhRecordFields{SubDomain: "ovh", FieldType: "NS", TTL: 10, Target: "203.0.113.42"}}})
|
||||
client.AssertExpectations(t)
|
||||
|
||||
// Error on getting zones list
|
||||
|
@ -71,7 +71,7 @@ func TestRcodeZeroProvider_Records(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
require.Equal(t, 6, len(endpoints))
|
||||
require.Equal(t, 10, len(endpoints))
|
||||
|
||||
mockRRSetService.TestErrorReturned = true
|
||||
|
||||
|
@ -17,10 +17,10 @@ limitations under the License.
|
||||
package provider
|
||||
|
||||
// SupportedRecordType returns true only for supported record types.
|
||||
// Currently A, CNAME, SRV, and TXT record types are supported.
|
||||
// Currently A, CNAME, SRV, TXT and NS record types are supported.
|
||||
func SupportedRecordType(recordType string) bool {
|
||||
switch recordType {
|
||||
case "A", "CNAME", "SRV", "TXT":
|
||||
case "A", "CNAME", "SRV", "TXT", "NS":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
29
provider/scaleway/interface.go
Normal file
29
provider/scaleway/interface.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2020 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 scaleway
|
||||
|
||||
import (
|
||||
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
|
||||
"github.com/scaleway/scaleway-sdk-go/scw"
|
||||
)
|
||||
|
||||
// DomainAPI is an interface matching the domain.API struct
|
||||
type DomainAPI interface {
|
||||
ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error)
|
||||
ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error)
|
||||
UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error)
|
||||
}
|
337
provider/scaleway/scaleway.go
Normal file
337
provider/scaleway/scaleway.go
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
Copyright 2020 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 scaleway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
|
||||
"github.com/scaleway/scaleway-sdk-go/scw"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
scalewyRecordTTL uint32 = 300
|
||||
scalewayDefaultPriority uint32 = 0
|
||||
scalewayPriorityKey string = "scw/priority"
|
||||
)
|
||||
|
||||
// ScalewayProvider implements the DNS provider for Scaleway DNS
|
||||
type ScalewayProvider struct {
|
||||
provider.BaseProvider
|
||||
domainAPI DomainAPI
|
||||
dryRun bool
|
||||
// only consider hosted zones managing domains ending in this suffix
|
||||
domainFilter endpoint.DomainFilter
|
||||
}
|
||||
|
||||
// ScalewayChange differentiates between ChangActions
|
||||
type ScalewayChange struct {
|
||||
Action string
|
||||
Record []domain.Record
|
||||
}
|
||||
|
||||
// NewScalewayProvider initializes a new Scaleway DNS provider
|
||||
func NewScalewayProvider(ctx context.Context, domainFilter endpoint.DomainFilter, dryRun bool) (*ScalewayProvider, error) {
|
||||
scwClient, err := scw.NewClient(
|
||||
scw.WithEnv(),
|
||||
scw.WithUserAgent("ExternalDNS/"+externaldns.Version),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := scwClient.GetDefaultOrganizationID(); !ok {
|
||||
return nil, fmt.Errorf("default organization is not set")
|
||||
}
|
||||
|
||||
if _, ok := scwClient.GetAccessKey(); !ok {
|
||||
return nil, fmt.Errorf("access key no set")
|
||||
}
|
||||
|
||||
if _, ok := scwClient.GetSecretKey(); !ok {
|
||||
return nil, fmt.Errorf("secret key no set")
|
||||
}
|
||||
|
||||
domainAPI := domain.NewAPI(scwClient)
|
||||
|
||||
return &ScalewayProvider{
|
||||
domainAPI: domainAPI,
|
||||
dryRun: dryRun,
|
||||
domainFilter: domainFilter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Zones returns the list of hosted zones.
|
||||
func (p *ScalewayProvider) Zones(ctx context.Context) ([]*domain.DNSZone, error) {
|
||||
res := []*domain.DNSZone{}
|
||||
|
||||
dnsZones, err := p.domainAPI.ListDNSZones(&domain.ListDNSZonesRequest{}, scw.WithAllPages(), scw.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, dnsZone := range dnsZones.DNSZones {
|
||||
if p.domainFilter.Match(getCompleteZoneName(dnsZone)) {
|
||||
res = append(res, dnsZone)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Records returns the list of records in a given zone.
|
||||
func (p *ScalewayProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
endpoints := map[string]*endpoint.Endpoint{}
|
||||
dnsZones, err := p.Zones(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, zone := range dnsZones {
|
||||
recordsResp, err := p.domainAPI.ListDNSZoneRecords(&domain.ListDNSZoneRecordsRequest{
|
||||
DNSZone: getCompleteZoneName(zone),
|
||||
}, scw.WithAllPages())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range recordsResp.Records {
|
||||
name := record.Name + "."
|
||||
|
||||
// trim any leading or ending dot
|
||||
fullRecordName := strings.Trim(name+getCompleteZoneName(zone), ".")
|
||||
|
||||
if !provider.SupportedRecordType(record.Type.String()) {
|
||||
log.Infof("Skipping record %s because type %s is not supported", fullRecordName, record.Type.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// in external DNS, same endpoint have the same ttl and same priority
|
||||
// it's not the case in Scaleway DNS. It should never happen, but if
|
||||
// the record is modified without going through ExternalDNS, we could have
|
||||
// different priorities of ttls for a same name.
|
||||
// In this case, we juste take the first one.
|
||||
if existingEndpoint, ok := endpoints[record.Type.String()+"/"+fullRecordName]; ok {
|
||||
existingEndpoint.Targets = append(existingEndpoint.Targets, record.Data)
|
||||
log.Infof("Appending target %s to record %s, using TTL and priotiry of target %s", record.Data, fullRecordName, existingEndpoint.Targets[0])
|
||||
} else {
|
||||
ep := endpoint.NewEndpointWithTTL(fullRecordName, record.Type.String(), endpoint.TTL(record.TTL), record.Data)
|
||||
ep = ep.WithProviderSpecific(scalewayPriorityKey, fmt.Sprintf("%d", record.Priority))
|
||||
endpoints[record.Type.String()+"/"+fullRecordName] = ep
|
||||
}
|
||||
}
|
||||
}
|
||||
returnedEndpoints := []*endpoint.Endpoint{}
|
||||
for _, ep := range endpoints {
|
||||
returnedEndpoints = append(returnedEndpoints, ep)
|
||||
}
|
||||
|
||||
return returnedEndpoints, nil
|
||||
}
|
||||
|
||||
// ApplyChanges applies a set of changes in a zone.
|
||||
func (p *ScalewayProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
requests, err := p.generateApplyRequests(ctx, changes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, req := range requests {
|
||||
logChanges(req)
|
||||
if p.dryRun {
|
||||
log.Info("Running in dry run mode")
|
||||
continue
|
||||
}
|
||||
_, err := p.domainAPI.UpdateDNSZoneRecords(req, scw.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ScalewayProvider) generateApplyRequests(ctx context.Context, changes *plan.Changes) ([]*domain.UpdateDNSZoneRecordsRequest, error) {
|
||||
returnedRequests := []*domain.UpdateDNSZoneRecordsRequest{}
|
||||
recordsToAdd := map[string]*domain.RecordChangeAdd{}
|
||||
recordsToDelete := map[string][]*domain.RecordChange{}
|
||||
|
||||
dnsZones, err := p.Zones(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zoneNameMapper := provider.ZoneIDName{}
|
||||
for _, zone := range dnsZones {
|
||||
zoneName := getCompleteZoneName(zone)
|
||||
zoneNameMapper.Add(zoneName, zoneName)
|
||||
recordsToAdd[zoneName] = &domain.RecordChangeAdd{
|
||||
Records: []*domain.Record{},
|
||||
}
|
||||
recordsToDelete[zoneName] = []*domain.RecordChange{}
|
||||
}
|
||||
|
||||
for _, c := range changes.UpdateOld {
|
||||
zone, _ := zoneNameMapper.FindZone(c.DNSName)
|
||||
if zone == "" {
|
||||
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
|
||||
continue
|
||||
}
|
||||
recordsToDelete[zone] = append(recordsToDelete[zone], endpointToScalewayRecordsChangeDelete(zone, c)...)
|
||||
}
|
||||
|
||||
for _, c := range changes.Delete {
|
||||
zone, _ := zoneNameMapper.FindZone(c.DNSName)
|
||||
if zone == "" {
|
||||
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
|
||||
continue
|
||||
}
|
||||
recordsToDelete[zone] = append(recordsToDelete[zone], endpointToScalewayRecordsChangeDelete(zone, c)...)
|
||||
}
|
||||
|
||||
for _, c := range changes.Create {
|
||||
zone, _ := zoneNameMapper.FindZone(c.DNSName)
|
||||
if zone == "" {
|
||||
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
|
||||
continue
|
||||
}
|
||||
recordsToAdd[zone].Records = append(recordsToAdd[zone].Records, endpointToScalewayRecords(zone, c)...)
|
||||
}
|
||||
for _, c := range changes.UpdateNew {
|
||||
zone, _ := zoneNameMapper.FindZone(c.DNSName)
|
||||
if zone == "" {
|
||||
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
|
||||
continue
|
||||
}
|
||||
recordsToAdd[zone].Records = append(recordsToAdd[zone].Records, endpointToScalewayRecords(zone, c)...)
|
||||
}
|
||||
|
||||
for _, zone := range dnsZones {
|
||||
zoneName := getCompleteZoneName(zone)
|
||||
req := &domain.UpdateDNSZoneRecordsRequest{
|
||||
DNSZone: zoneName,
|
||||
Changes: recordsToDelete[zoneName],
|
||||
}
|
||||
req.Changes = append(req.Changes, &domain.RecordChange{
|
||||
Add: recordsToAdd[zoneName],
|
||||
})
|
||||
returnedRequests = append(returnedRequests, req)
|
||||
}
|
||||
|
||||
return returnedRequests, nil
|
||||
}
|
||||
|
||||
func getCompleteZoneName(zone *domain.DNSZone) string {
|
||||
subdomain := zone.Subdomain + "."
|
||||
if zone.Subdomain == "" {
|
||||
subdomain = ""
|
||||
}
|
||||
return subdomain + zone.Domain
|
||||
}
|
||||
|
||||
func endpointToScalewayRecords(zoneName string, ep *endpoint.Endpoint) []*domain.Record {
|
||||
// no annotation results in a TTL of 0, default to 300 for consistency with other providers
|
||||
var ttl = scalewyRecordTTL
|
||||
if ep.RecordTTL.IsConfigured() {
|
||||
ttl = uint32(ep.RecordTTL)
|
||||
}
|
||||
var priority = scalewayDefaultPriority
|
||||
if prop, ok := ep.GetProviderSpecificProperty(scalewayPriorityKey); ok {
|
||||
prio, err := strconv.ParseUint(prop.Value, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Failed parsing value of %s: %s: %v; using priority of %d", scalewayPriorityKey, prop.Value, err, scalewayDefaultPriority)
|
||||
} else {
|
||||
priority = uint32(prio)
|
||||
}
|
||||
}
|
||||
|
||||
records := []*domain.Record{}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
records = append(records, &domain.Record{
|
||||
Data: target,
|
||||
Name: strings.Trim(strings.TrimSuffix(ep.DNSName, zoneName), ". "),
|
||||
Priority: priority,
|
||||
TTL: ttl,
|
||||
Type: domain.RecordType(ep.RecordType),
|
||||
})
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
|
||||
func endpointToScalewayRecordsChangeDelete(zoneName string, ep *endpoint.Endpoint) []*domain.RecordChange {
|
||||
records := []*domain.RecordChange{}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
records = append(records, &domain.RecordChange{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: target,
|
||||
Name: strings.Trim(strings.TrimSuffix(ep.DNSName, zoneName), ". "),
|
||||
Type: domain.RecordType(ep.RecordType),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
|
||||
func logChanges(req *domain.UpdateDNSZoneRecordsRequest) {
|
||||
log.Infof("Updating zone %s", req.DNSZone)
|
||||
if !log.IsLevelEnabled(log.InfoLevel) {
|
||||
return
|
||||
}
|
||||
for _, change := range req.Changes {
|
||||
if change.Add != nil {
|
||||
for _, add := range change.Add.Records {
|
||||
name := add.Name + "."
|
||||
if add.Name == "" {
|
||||
name = ""
|
||||
}
|
||||
|
||||
logFields := log.Fields{
|
||||
"record": name + req.DNSZone,
|
||||
"type": add.Type.String(),
|
||||
"ttl": add.TTL,
|
||||
"priority": add.Priority,
|
||||
"data": add.Data,
|
||||
}
|
||||
log.WithFields(logFields).Info("Adding record")
|
||||
}
|
||||
} else if change.Delete != nil {
|
||||
name := change.Delete.Name + "."
|
||||
if change.Delete.Name == "" {
|
||||
name = ""
|
||||
}
|
||||
|
||||
logFields := log.Fields{
|
||||
"record": name + req.DNSZone,
|
||||
"type": change.Delete.Type.String(),
|
||||
"data": change.Delete.Data,
|
||||
}
|
||||
|
||||
log.WithFields(logFields).Info("Deleting record")
|
||||
}
|
||||
}
|
||||
}
|
517
provider/scaleway/scaleway_test.go
Normal file
517
provider/scaleway/scaleway_test.go
Normal file
@ -0,0 +1,517 @@
|
||||
/*
|
||||
Copyright 2020 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 scaleway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
|
||||
"github.com/scaleway/scaleway-sdk-go/scw"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockScalewayDomain struct {
|
||||
*domain.API
|
||||
}
|
||||
|
||||
func (m *mockScalewayDomain) ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error) {
|
||||
return &domain.ListDNSZonesResponse{
|
||||
DNSZones: []*domain.DNSZone{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Subdomain: "",
|
||||
},
|
||||
{
|
||||
Domain: "example.com",
|
||||
Subdomain: "test",
|
||||
},
|
||||
{
|
||||
Domain: "dummy.me",
|
||||
Subdomain: "",
|
||||
},
|
||||
{
|
||||
Domain: "dummy.me",
|
||||
Subdomain: "test",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockScalewayDomain) ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error) {
|
||||
records := []*domain.Record{}
|
||||
if req.DNSZone == "example.com" {
|
||||
records = []*domain.Record{
|
||||
{
|
||||
Data: "1.1.1.1",
|
||||
Name: "one",
|
||||
TTL: 300,
|
||||
Priority: 0,
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
{
|
||||
Data: "1.1.1.2",
|
||||
Name: "two",
|
||||
TTL: 300,
|
||||
Priority: 0,
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
{
|
||||
Data: "1.1.1.3",
|
||||
Name: "two",
|
||||
TTL: 300,
|
||||
Priority: 0,
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
}
|
||||
} else if req.DNSZone == "test.example.com" {
|
||||
records = []*domain.Record{
|
||||
{
|
||||
Data: "1.1.1.1",
|
||||
Name: "",
|
||||
TTL: 300,
|
||||
Priority: 0,
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
{
|
||||
Data: "test.example.com",
|
||||
Name: "two",
|
||||
TTL: 600,
|
||||
Priority: 30,
|
||||
Type: domain.RecordTypeCNAME,
|
||||
},
|
||||
}
|
||||
}
|
||||
return &domain.ListDNSZoneRecordsResponse{
|
||||
Records: records,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockScalewayDomain) UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestScalewayProvider_NewScalewayProvider(t *testing.T) {
|
||||
_ = os.Setenv(scw.ScwAccessKeyEnv, "SCWXXXXXXXXXXXXXXXXX")
|
||||
_ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111")
|
||||
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "11111111-1111-1111-1111-111111111111")
|
||||
_, err := NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err != nil {
|
||||
t.Errorf("failed : %s", err)
|
||||
}
|
||||
|
||||
_ = os.Unsetenv(scw.ScwDefaultOrganizationIDEnv)
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
|
||||
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "dummy")
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
|
||||
_ = os.Unsetenv(scw.ScwSecretKeyEnv)
|
||||
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "11111111-1111-1111-1111-111111111111")
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
|
||||
_ = os.Setenv(scw.ScwSecretKeyEnv, "dummy")
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
|
||||
_ = os.Unsetenv(scw.ScwAccessKeyEnv)
|
||||
_ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111")
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
|
||||
_ = os.Setenv(scw.ScwAccessKeyEnv, "dummy")
|
||||
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
|
||||
if err == nil {
|
||||
t.Errorf("expected to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalewayProvider_Zones(t *testing.T) {
|
||||
mocked := mockScalewayDomain{nil}
|
||||
provider := &ScalewayProvider{
|
||||
domainAPI: &mocked,
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
expected := []*domain.DNSZone{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Subdomain: "",
|
||||
},
|
||||
{
|
||||
Domain: "example.com",
|
||||
Subdomain: "test",
|
||||
},
|
||||
}
|
||||
zones, err := provider.Zones(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.Len(t, zones, len(expected))
|
||||
for i, zone := range zones {
|
||||
assert.Equal(t, expected[i], zone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalewayProvider_Records(t *testing.T) {
|
||||
mocked := mockScalewayDomain{nil}
|
||||
provider := &ScalewayProvider{
|
||||
domainAPI: &mocked,
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
expected := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "one.example.com",
|
||||
RecordTTL: 300,
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.1"},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "two.example.com",
|
||||
RecordTTL: 300,
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.2", "1.1.1.3"},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "test.example.com",
|
||||
RecordTTL: 300,
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.1"},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "two.test.example.com",
|
||||
RecordTTL: 600,
|
||||
RecordType: "CNAME",
|
||||
Targets: []string{"test.example.com"},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "30",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
records, err := provider.Records(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
require.Len(t, records, len(expected))
|
||||
for _, record := range records {
|
||||
found := false
|
||||
for _, expectedRecord := range expected {
|
||||
if checkRecordEquality(record, expectedRecord) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, found)
|
||||
}
|
||||
}
|
||||
|
||||
// this test is really ugly since we are working on maps, so array are randomly sorted
|
||||
// feel free to modify if you have a better idea
|
||||
func TestScalewayProvider_generateApplyRequests(t *testing.T) {
|
||||
mocked := mockScalewayDomain{nil}
|
||||
provider := &ScalewayProvider{
|
||||
domainAPI: &mocked,
|
||||
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
expected := []*domain.UpdateDNSZoneRecordsRequest{
|
||||
{
|
||||
DNSZone: "example.com",
|
||||
Changes: []*domain.RecordChange{
|
||||
{
|
||||
Add: &domain.RecordChangeAdd{
|
||||
Records: []*domain.Record{
|
||||
{
|
||||
Data: "1.1.1.1",
|
||||
Name: "",
|
||||
TTL: 300,
|
||||
Type: domain.RecordTypeA,
|
||||
Priority: 0,
|
||||
},
|
||||
{
|
||||
Data: "1.1.1.2",
|
||||
Name: "",
|
||||
TTL: 300,
|
||||
Type: domain.RecordTypeA,
|
||||
Priority: 0,
|
||||
},
|
||||
{
|
||||
Data: "2.2.2.2",
|
||||
Name: "me",
|
||||
TTL: 600,
|
||||
Type: domain.RecordTypeA,
|
||||
Priority: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "3.3.3.3",
|
||||
Name: "me",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "1.1.1.1",
|
||||
Name: "here",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "1.1.1.2",
|
||||
Name: "here",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSZone: "test.example.com",
|
||||
Changes: []*domain.RecordChange{
|
||||
{
|
||||
Add: &domain.RecordChangeAdd{
|
||||
Records: []*domain.Record{
|
||||
{
|
||||
Data: "example.com",
|
||||
Name: "",
|
||||
TTL: 600,
|
||||
Type: domain.RecordTypeCNAME,
|
||||
Priority: 20,
|
||||
},
|
||||
{
|
||||
Data: "1.2.3.4",
|
||||
Name: "my",
|
||||
TTL: 300,
|
||||
Type: domain.RecordTypeA,
|
||||
Priority: 0,
|
||||
},
|
||||
{
|
||||
Data: "5.6.7.8",
|
||||
Name: "my",
|
||||
TTL: 300,
|
||||
Type: domain.RecordTypeA,
|
||||
Priority: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "1.1.1.1",
|
||||
Name: "here.is.my",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "4.4.4.4",
|
||||
Name: "my",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
{
|
||||
Delete: &domain.RecordChangeDelete{
|
||||
Data: "5.5.5.5",
|
||||
Name: "my",
|
||||
Type: domain.RecordTypeA,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.com",
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.1", "1.1.1.2"},
|
||||
},
|
||||
{
|
||||
DNSName: "test.example.com",
|
||||
RecordType: "CNAME",
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "20",
|
||||
},
|
||||
},
|
||||
RecordTTL: 600,
|
||||
Targets: []string{"example.com"},
|
||||
},
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "here.example.com",
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.1", "1.1.1.2"},
|
||||
},
|
||||
{
|
||||
DNSName: "here.is.my.test.example.com",
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.1.1.1"},
|
||||
},
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "me.example.com",
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "30",
|
||||
},
|
||||
},
|
||||
RecordType: "A",
|
||||
RecordTTL: 600,
|
||||
Targets: []string{"2.2.2.2"},
|
||||
},
|
||||
{
|
||||
DNSName: "my.test.example.com",
|
||||
RecordType: "A",
|
||||
Targets: []string{"1.2.3.4", "5.6.7.8"},
|
||||
},
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "me.example.com",
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
{
|
||||
Name: scalewayPriorityKey,
|
||||
Value: "1234",
|
||||
},
|
||||
},
|
||||
RecordType: "A",
|
||||
Targets: []string{"3.3.3.3"},
|
||||
},
|
||||
{
|
||||
DNSName: "my.test.example.com",
|
||||
RecordType: "A",
|
||||
Targets: []string{"4.4.4.4", "5.5.5.5"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
requests, err := provider.generateApplyRequests(context.TODO(), changes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
require.Len(t, requests, len(expected))
|
||||
total := int(len(expected))
|
||||
for _, req := range requests {
|
||||
for _, exp := range expected {
|
||||
if checkScalewayReqChanges(req, exp) {
|
||||
total--
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(t, 0, total)
|
||||
}
|
||||
|
||||
func checkRecordEquality(record1, record2 *endpoint.Endpoint) bool {
|
||||
return record1.Targets.Same(record2.Targets) &&
|
||||
record1.DNSName == record2.DNSName &&
|
||||
record1.RecordTTL == record2.RecordTTL &&
|
||||
record1.RecordType == record2.RecordType &&
|
||||
reflect.DeepEqual(record1.ProviderSpecific, record2.ProviderSpecific)
|
||||
}
|
||||
|
||||
func checkScalewayReqChanges(r1, r2 *domain.UpdateDNSZoneRecordsRequest) bool {
|
||||
if r1.DNSZone != r2.DNSZone {
|
||||
return false
|
||||
}
|
||||
if len(r1.Changes) != len(r2.Changes) {
|
||||
return false
|
||||
}
|
||||
total := int(len(r1.Changes))
|
||||
for _, c1 := range r1.Changes {
|
||||
for _, c2 := range r2.Changes {
|
||||
// we only have 1 add per request
|
||||
if c1.Add != nil && c2.Add != nil && checkScalewayRecords(c1.Add.Records, c2.Add.Records) {
|
||||
total--
|
||||
} else if c1.Delete != nil && c2.Delete != nil {
|
||||
if c1.Delete.Data == c2.Delete.Data && c1.Delete.Name == c2.Delete.Name && c1.Delete.Type == c2.Delete.Type {
|
||||
total--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total == 0
|
||||
}
|
||||
|
||||
func checkScalewayRecords(rs1, rs2 []*domain.Record) bool {
|
||||
if len(rs1) != len(rs2) {
|
||||
return false
|
||||
}
|
||||
total := int(len(rs1))
|
||||
for _, r1 := range rs1 {
|
||||
for _, r2 := range rs2 {
|
||||
if r1.Data == r2.Data &&
|
||||
r1.Name == r2.Name &&
|
||||
r1.Priority == r2.Priority &&
|
||||
r1.TTL == r2.TTL &&
|
||||
r1.Type == r2.Type {
|
||||
total--
|
||||
}
|
||||
}
|
||||
}
|
||||
return total == 0
|
||||
}
|
@ -24,6 +24,7 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
udnssdk "github.com/ultradns/ultradns-sdk-go"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vultr/govultr"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
|
@ -238,6 +238,9 @@ func (pr affixNameMapper) toEndpointName(txtDNSName string) string {
|
||||
|
||||
func (pr affixNameMapper) toTXTName(endpointDNSName string) string {
|
||||
DNSName := strings.SplitN(endpointDNSName, ".", 2)
|
||||
if len(DNSName) < 2 {
|
||||
return pr.prefix + DNSName[0] + pr.suffix
|
||||
}
|
||||
return pr.prefix + DNSName[0] + pr.suffix + "." + DNSName[1]
|
||||
}
|
||||
|
||||
|
@ -400,6 +400,7 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) {
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", "", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", "", "", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", "", "", "ingress/default/my-ingress"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
@ -420,6 +421,8 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) {
|
||||
newEndpointWithOwner("txt.new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, ""),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", "", "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwner("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", "", "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwner("txt.example", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
@ -491,6 +494,7 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) {
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", "", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", "", "", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", "", "", "ingress/default/my-ingress"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
@ -511,6 +515,8 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) {
|
||||
newEndpointWithOwner("new-record-1-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, ""),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", "", "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwner("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", "", "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwner("example-txt", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
@ -577,6 +583,7 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) {
|
||||
changes := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""),
|
||||
newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
@ -592,6 +599,8 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) {
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""),
|
||||
newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwner("example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
|
6
scripts/kustomize-version-udapter.sh
Executable file
6
scripts/kustomize-version-udapter.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
sed -i -e "s/newTag: .*/newTag: $1/g" kustomize/kustomization.yaml
|
||||
git add kustomize/kustomization.yaml
|
||||
git commit -sm "updates kustomize with newly released version"
|
35
scripts/releaser.sh
Executable file
35
scripts/releaser.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
current_tag="${GITHUB_REF#refs/tags/}"
|
||||
start_ref="HEAD"
|
||||
|
||||
function generate_changelog {
|
||||
# Find the previous release on the same branch, skipping prereleases if the
|
||||
# current tag is a full release
|
||||
previous_tag=""
|
||||
while [[ -z $previous_tag || ( $previous_tag == *-* && $current_tag != *-* ) ]]; do
|
||||
previous_tag="$(git describe --tags "$start_ref"^ --abbrev=0)"
|
||||
start_ref="$previous_tag"
|
||||
done
|
||||
|
||||
git log "$previous_tag".. --reverse --merges --oneline --grep='Merge pull request #' | \
|
||||
while read -r sha title; do
|
||||
pr_num="$(grep -o '#[[:digit:]]\+' <<<"$title")"
|
||||
pr_desc="$(git show -s --format=%b "$sha" | sed -n '1,/^$/p' | tr $'\n' ' ')"
|
||||
pr_author="$(gh pr view "$pr_num" | grep author | awk '{ print $2 }' | tr $'\n' ' ')"
|
||||
printf "* %s (%s) @%s\n\n" "$pr_desc" "$pr_num" "$pr_author"
|
||||
done
|
||||
}
|
||||
|
||||
function create_release {
|
||||
generate_changelog | gh release create "$1" -t "$1" -F -
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "$0: usage: releaser [release number]"
|
||||
echo "example: ./releaser.sh v0.7.5"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_release "$1"
|
@ -43,6 +43,7 @@ type crdSource struct {
|
||||
crdResource string
|
||||
codec runtime.ParameterCodec
|
||||
annotationFilter string
|
||||
labelFilter string
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error {
|
||||
@ -102,11 +103,12 @@ func NewCRDClientForAPIVersionKind(client kubernetes.Interface, kubeConfig, apiS
|
||||
}
|
||||
|
||||
// NewCRDSource creates a new crdSource with the given config.
|
||||
func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, scheme *runtime.Scheme) (Source, error) {
|
||||
func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelFilter string, scheme *runtime.Scheme) (Source, error) {
|
||||
return &crdSource{
|
||||
crdResource: strings.ToLower(kind) + "s",
|
||||
namespace: namespace,
|
||||
annotationFilter: annotationFilter,
|
||||
labelFilter: labelFilter,
|
||||
crdClient: crdClient,
|
||||
codec: runtime.NewParameterCodec(scheme),
|
||||
}, nil
|
||||
@ -119,12 +121,22 @@ func (cs *crdSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||
func (cs *crdSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
endpoints := []*endpoint.Endpoint{}
|
||||
|
||||
result, err := cs.List(ctx, &metav1.ListOptions{})
|
||||
var (
|
||||
result *endpoint.DNSEndpointList
|
||||
err error
|
||||
)
|
||||
|
||||
if cs.labelFilter != "" {
|
||||
result, err = cs.List(ctx, &metav1.ListOptions{LabelSelector: cs.labelFilter})
|
||||
} else {
|
||||
result, err = cs.List(ctx, &metav1.ListOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err = cs.filterByAnnotations(result)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user