mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Merge branch 'master' into docs_apps/v1_deployment
This commit is contained in:
commit
b78d472940
63
.github/labeler.yml
vendored
Normal file
63
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# Add 'docs' to any changes within 'docs' folder or any subfolders
|
||||
docs:
|
||||
- docs/**/*
|
||||
|
||||
# Add 'provider/alibaba' in file which starts with alibaba
|
||||
provider/alibaba: provider/alibaba*
|
||||
|
||||
# Add 'provider/aws' in file which starts with aws
|
||||
provider/aws: provider/aws*
|
||||
|
||||
# Add 'provider/azure' in file which starts with azure
|
||||
provider/azure: provider/azure*
|
||||
|
||||
# Add 'provider/cloudflare' in file which starts with cloudflare
|
||||
provider/cloudflare: provider/cloudflare*
|
||||
|
||||
# Add 'provider/coredns' in file which starts with coredns
|
||||
provider/coredns: provider/coredns*
|
||||
|
||||
# Add 'provider/designate' in file which starts with designate
|
||||
provider/designate: provider/designate*
|
||||
|
||||
# Add 'provider/digitalocean' in file which starts with digitalocean
|
||||
provider/digitalocean: provider/digital_ocean*
|
||||
|
||||
# Add 'provider/dnssimple' in file which starts with dnssimple
|
||||
provider/dnssimple: provider/dnssimple*
|
||||
|
||||
# Add 'provider/dyn' in file which starts with dyn
|
||||
provider/dyn: provider/dyn*
|
||||
|
||||
# Add 'provider/exoscale' in file which starts with exoscale
|
||||
provider/exoscale: provider/exoscale*
|
||||
|
||||
# Add 'provider/transip' in file which starts with transip
|
||||
provider/transip: provider/transip*
|
||||
|
||||
# Add 'provider/rfc2136' in file which starts with rfc2136
|
||||
provider/rfc2136: provider/rfc2136*
|
||||
|
||||
# Add 'provider/rdns' in file which starts with rdns
|
||||
provider/rdns: provider/rdns*
|
||||
|
||||
# Add 'provider/powerdns' in file which starts with pdns
|
||||
provider/powerdns: provider/pdns*
|
||||
|
||||
# Add 'provider/google' in file which starts with google
|
||||
provider/google: provider/google*
|
||||
|
||||
# Add 'provider/infoblox' in file which starts with infoblox
|
||||
provider/infoblox: provider/infoblox*
|
||||
|
||||
# Add 'provider/linode' in file which starts with linode
|
||||
provider/linode: provider/linode*
|
||||
|
||||
# Add 'provider/ns1' in file which starts with ns1
|
||||
provider/ns1: provider/ns1*
|
||||
|
||||
# Add 'provider/oci' in file which starts with oci
|
||||
provider/oci: provider/oci*
|
||||
|
||||
# Add 'provider/vinyldns' in file which starts with vinyldns
|
||||
provider/vinyldns: provider/vinyldns*
|
@ -14,7 +14,7 @@ matrix:
|
||||
- go: tip
|
||||
|
||||
env:
|
||||
- GOLANGCI_RELEASE="v1.17.1"
|
||||
- GOLANGCI_RELEASE="v1.21.0"
|
||||
|
||||
before_install:
|
||||
- GO111MODULE=off go get github.com/mattn/goveralls
|
||||
|
@ -15,7 +15,7 @@
|
||||
# builder image
|
||||
FROM golang:1.13 as builder
|
||||
|
||||
WORKDIR /github.com/kubernetes-incubator/external-dns
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
|
||||
COPY . .
|
||||
RUN go mod vendor && \
|
||||
@ -29,7 +29,7 @@ LABEL maintainer="Team Teapot @ Zalando SE <team-teapot@zalando.de>"
|
||||
RUN apk add --no-cache ca-certificates && \
|
||||
update-ca-certificates
|
||||
|
||||
COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns
|
||||
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:
|
||||
# https://github.com/kubernetes/kubernetes/issues/40958
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
FROM golang:1.13 as builder
|
||||
|
||||
WORKDIR /github.com/kubernetes-incubator/external-dns
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
|
||||
COPY . .
|
||||
RUN apt-get update && \
|
||||
@ -27,7 +27,7 @@ RUN apt-get update && \
|
||||
FROM gcr.io/distroless/static
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns
|
||||
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:
|
||||
# https://github.com/kubernetes/kubernetes/issues/40958
|
||||
|
16
Makefile
16
Makefile
@ -31,7 +31,7 @@ cover-html: cover
|
||||
|
||||
# Run all the linters
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
golangci-lint run --timeout=5m ./...
|
||||
|
||||
|
||||
# The verify target runs tasks similar to the CI tasks, but without code coverage
|
||||
@ -45,10 +45,11 @@ test:
|
||||
|
||||
BINARY ?= external-dns
|
||||
SOURCES = $(shell find . -name '*.go')
|
||||
IMAGE ?= registry.opensource.zalan.do/teapot/$(BINARY)
|
||||
IMAGE_STAGING = gcr.io/k8s-staging-external-dns/$(BINARY)
|
||||
IMAGE ?= us.gcr.io/k8s-artifacts-prod/external-dns/$(BINARY)
|
||||
VERSION ?= $(shell git describe --tags --always --dirty)
|
||||
BUILD_FLAGS ?= -v
|
||||
LDFLAGS ?= -X github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s
|
||||
LDFLAGS ?= -X sigs.k8s.io/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s
|
||||
|
||||
build: build/$(BINARY)
|
||||
|
||||
@ -66,3 +67,12 @@ build.mini:
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
# Builds and push container images to the staging bucket.
|
||||
.PHONY: release.staging
|
||||
|
||||
release.staging:
|
||||
IMAGE=$(IMAGE_STAGING) $(MAKE) build.docker build.push
|
||||
|
||||
release.prod:
|
||||
$(MAKE) build.docker build.push
|
32
README.md
32
README.md
@ -3,11 +3,11 @@
|
||||
</p>
|
||||
|
||||
# ExternalDNS
|
||||
[](https://travis-ci.org/kubernetes-incubator/external-dns)
|
||||
[](https://coveralls.io/github/kubernetes-incubator/external-dns?branch=master)
|
||||
[](https://github.com/kubernetes-incubator/external-dns/releases)
|
||||
[](https://godoc.org/github.com/kubernetes-incubator/external-dns)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-incubator/external-dns)
|
||||
[](https://travis-ci.org/kubernetes-sigs/external-dns)
|
||||
[](https://coveralls.io/github/kubernetes-sigs/external-dns?branch=master)
|
||||
[](https://github.com/kubernetes-sigs/external-dns/releases)
|
||||
[](https://godoc.org/github.com/kubernetes-sigs/external-dns)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/external-dns)
|
||||
|
||||
ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
|
||||
|
||||
@ -53,7 +53,7 @@ Note that all flags can be replaced with environment variables; for instance,
|
||||
|
||||
## Status of providers
|
||||
|
||||
ExternalDNS supports multiple DNS providers which have been implemented by the [ExternalDNS contributors](https://github.com/kubernetes-incubator/external-dns/graphs/contributors). Maintaining all of those in a central repository is a challenge and we have limited resources to test changes. This means that it is very hard to test all providers for possible regressions and, as written in the [Contributing](#Contributing) section, we encourage contributors to step in as maintainers for the individual providers and help by testing the integrations.
|
||||
ExternalDNS supports multiple DNS providers which have been implemented by the [ExternalDNS contributors](https://github.com/kubernetes-sigs/external-dns/graphs/contributors). Maintaining all of those in a central repository is a challenge and we have limited resources to test changes. This means that it is very hard to test all providers for possible regressions and, as written in the [Contributing](#Contributing) section, we encourage contributors to step in as maintainers for the individual providers and help by testing the integrations.
|
||||
|
||||
End-to-end testing of ExternalDNS is currently
|
||||
[performed](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/test/e2e/external_dns.go)
|
||||
@ -110,7 +110,8 @@ The following tutorials are provided:
|
||||
* [Route53](docs/tutorials/aws.md)
|
||||
* [Same domain for public and private Route53 zones](docs/tutorials/public-private-route53.md)
|
||||
* [Service Discovery](docs/tutorials/aws-sd.md)
|
||||
* [Azure](docs/tutorials/azure.md)
|
||||
* [Azure DNS](docs/tutorials/azure.md)
|
||||
* [Azure Private DNS](docs/tutorials/azure-private-dns.md)
|
||||
* [Cloudflare](docs/tutorials/cloudflare.md)
|
||||
* [CoreDNS](docs/tutorials/coredns.md)
|
||||
* [DigitalOcean](docs/tutorials/digitalocean.md)
|
||||
@ -151,7 +152,7 @@ Make sure you have the following prerequisites:
|
||||
First, get ExternalDNS:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/kubernetes-incubator/external-dns.git && cd external-dns
|
||||
$ 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
|
||||
@ -261,6 +262,7 @@ Here's a rough outline on what is to come (subject to change):
|
||||
- [x] Support for RcodeZero
|
||||
- [x] Support for NS1
|
||||
- [x] Support for TransIP
|
||||
- [x] Support for Azure Private DNS
|
||||
|
||||
### v0.6
|
||||
|
||||
@ -281,7 +283,7 @@ Here's a rough outline on what is to come (subject to change):
|
||||
* Support for CRDs
|
||||
* Support for more advanced DNS record configurations
|
||||
|
||||
Have a look at [the milestones](https://github.com/kubernetes-incubator/external-dns/milestones) to get an idea of where we currently stand.
|
||||
Have a look at [the milestones](https://github.com/kubernetes-sigs/external-dns/milestones) to get an idea of where we currently stand.
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -313,18 +315,6 @@ ExternalDNS is an effort to unify the following similar projects in order to bri
|
||||
* Zalando's [Mate](https://github.com/linki/mate)
|
||||
* Molecule Software's [route53-kubernetes](https://github.com/wearemolecule/route53-kubernetes)
|
||||
|
||||
## Kubernetes Incubator
|
||||
|
||||
This is a [Kubernetes Incubator project](https://github.com/kubernetes/community/blob/master/incubator.md).
|
||||
The project was established 2017-Feb-9 (initial announcement [here](https://groups.google.com/forum/#!searchin/kubernetes-dev/external$20dns%7Csort:relevance/kubernetes-dev/2wGQUB0fUuE/9OXz01i2BgAJ)).
|
||||
The incubator team for the project is:
|
||||
|
||||
* Sponsor: sig-network
|
||||
* Champion: Tim Hockin (@thockin)
|
||||
* SIG: sig-network
|
||||
|
||||
For more information about sig-network, such as meeting times and agenda, check out the [community site](https://github.com/kubernetes/community/tree/master/sig-network).
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
||||
|
14
ci/postsubmit/push-to-staging/cloudbuild.yaml
Normal file
14
ci/postsubmit/push-to-staging/cloudbuild.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
# See https://cloud.google.com/cloud-build/docs/build-config
|
||||
timeout: 1200s
|
||||
steps:
|
||||
- name: "gcr.io/k8s-testimages/gcb-docker-gcloud:v20190906-745fed4"
|
||||
entrypoint: make
|
||||
env:
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
- TAG=$_GIT_TAG
|
||||
args:
|
||||
- release.staging
|
||||
substitutions:
|
||||
# _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and
|
||||
# can be used as a substitution
|
||||
_GIT_TAG: "12345"
|
@ -23,10 +23,10 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
"github.com/kubernetes-incubator/external-dns/registry"
|
||||
"github.com/kubernetes-incubator/external-dns/source"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
"sigs.k8s.io/external-dns/registry"
|
||||
"sigs.k8s.io/external-dns/source"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -22,11 +22,11 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
"github.com/kubernetes-incubator/external-dns/registry"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
"sigs.k8s.io/external-dns/registry"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -1,23 +0,0 @@
|
||||
version: "2017-09-20"
|
||||
pipeline:
|
||||
- id: build
|
||||
overlay: ci/golang
|
||||
cache:
|
||||
paths:
|
||||
- /go/pkg/mod # pkg cache for Go modules
|
||||
- ~/.cache/go-build # Go build cache
|
||||
type: script
|
||||
commands:
|
||||
- desc: build
|
||||
cmd: |
|
||||
make build.docker
|
||||
- desc: push
|
||||
cmd: |
|
||||
if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then
|
||||
IMAGE=registry-write.opensource.zalan.do/teapot/external-dns
|
||||
VERSION=$(git describe --tags --always --dirty)
|
||||
else
|
||||
IMAGE=registry-write.opensource.zalan.do/teapot/external-dns-test
|
||||
VERSION=$CDP_BUILD_VERSION
|
||||
fi
|
||||
IMAGE=$IMAGE VERSION=$VERSION make build.push
|
@ -2,7 +2,7 @@
|
||||
|
||||
<!-- TOC depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->
|
||||
|
||||
- [Move ExternalDNS out of Kubernetes incubator](#move-externaldns-out-of-kubernetes-incubator)
|
||||
- [Move ExternalDNS out of Kubernetes incubator](#move-externaldns-out-of-kubernetes-sigs)
|
||||
- [Summary](#summary)
|
||||
- [Motivation](#motivation)
|
||||
- [Goals](#goals)
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
## Summary
|
||||
|
||||
[ExternalDNS](https://github.com/kubernetes-incubator/external-dns) is a project that synchronizes Kubernetes’ Services, Ingresses and other Kubernetes resources to DNS backends for several DNS providers.
|
||||
[ExternalDNS](https://github.com/kubernetes-sigs/external-dns) is a project that synchronizes Kubernetes’ Services, Ingresses and other Kubernetes resources to DNS backends for several DNS providers.
|
||||
|
||||
The projects was started as a Kubernetes Incubator project in February 2017 and being the Kubernetes incubation initiative officially over, the maintainers want to propose the project to be moved to the kubernetes GitHub organization or to kubernetes-sigs, under the sponsorship of sig-network.
|
||||
|
||||
@ -35,7 +35,7 @@ When the project was proposed (see the [original discussion](https://github.com/
|
||||
|
||||
ExternalDNS’ goal from the beginning was to provide an officially supported solution to those problems.
|
||||
|
||||
After two years of development, the project is still in the kubernetes-incubator.
|
||||
After two years of development, the project is still in the kubernetes-sigs.
|
||||
|
||||
The incubation has been officially discontinued and to quote @thockin "Incubator projects should either become real projects in Kubernetes, shut themselves down, or move elsewhere" (see original thread [here](https://groups.google.com/forum/#!topic/kubernetes-sig-network/fvpDC_nxtEM)).
|
||||
|
||||
@ -57,7 +57,7 @@ External DNS...
|
||||
|
||||
* Supports already 18 different DNS providers including all major public clouds (AWS, Azure, GCP).
|
||||
|
||||
Given that the kubernetes-incubator organization will eventually be shut down, the possible alternatives to moving to be an official Kubernetes project are the following:
|
||||
Given that the kubernetes-sigs organization will eventually be shut down, the possible alternatives to moving to be an official Kubernetes project are the following:
|
||||
|
||||
* Shut down the project
|
||||
|
||||
|
@ -4,12 +4,12 @@ CRD source provides a generic mechanism to manage DNS records in your favourite
|
||||
|
||||
### Details
|
||||
|
||||
CRD source watches for a user specified CRD to extract [Endpoints](https://github.com/kubernetes-incubator/external-dns/blob/master/endpoint/endpoint.go) from its `Spec`.
|
||||
CRD source watches for a user specified CRD to extract [Endpoints](https://github.com/kubernetes-sigs/external-dns/blob/master/endpoint/endpoint.go) from its `Spec`.
|
||||
So users need to create such a CRD and register it to the kubernetes cluster and then create new object(s) of the CRD specifying the Endpoints.
|
||||
|
||||
### Registering CRD
|
||||
|
||||
Here is typical example of [CRD API type](https://github.com/kubernetes-incubator/external-dns/blob/master/endpoint/endpoint.go) which provides Endpoints to `CRD source`:
|
||||
Here is typical example of [CRD API type](https://github.com/kubernetes-sigs/external-dns/blob/master/endpoint/endpoint.go) which provides Endpoints to `CRD source`:
|
||||
|
||||
```go
|
||||
type TTL int64
|
||||
@ -106,3 +106,15 @@ INFO[0000] Connected to cluster at https://192.168.99.100:8443
|
||||
INFO[0000] CREATE: foo.bar.com 180 IN A 192.168.99.216
|
||||
INFO[0000] CREATE: foo.bar.com 0 IN TXT "heritage=external-dns,external-dns/owner=default"
|
||||
```
|
||||
|
||||
### RBAC configuration
|
||||
|
||||
If you use RBAC, extend the `external-dns` ClusterRole with:
|
||||
```
|
||||
- apiGroups: ["externaldns.k8s.io"]
|
||||
resources: ["dnsendpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["externaldns.k8s.io"]
|
||||
resources: ["dnsendpoints/status"]
|
||||
verbs: ["*"]
|
||||
```
|
||||
|
@ -60,8 +60,3 @@ spec:
|
||||
type: integer
|
||||
type: object
|
||||
version: v1alpha1
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: null
|
||||
|
@ -154,11 +154,11 @@ spec:
|
||||
|
||||
### I have a Service/Ingress but it's ignored by ExternalDNS. Why?
|
||||
|
||||
ExternalDNS can be configured to only use Services or Ingresses as source. In case Services or Ingresses seem to be ignored in your setup, consider checking how the flag `--source` was configured when deployed. For reference, see the issue https://github.com/kubernetes-incubator/external-dns/issues/267.
|
||||
ExternalDNS can be configured to only use Services or Ingresses as source. In case Services or Ingresses seem to be ignored in your setup, consider checking how the flag `--source` was configured when deployed. For reference, see the issue https://github.com/kubernetes-sigs/external-dns/issues/267.
|
||||
|
||||
### I'm using an ELB with TXT registry but the CNAME record clashes with the TXT record. How to avoid this?
|
||||
|
||||
CNAMEs cannot co-exist with other records, therefore you can use the `--txt-prefix` flag which makes sure to create a TXT record with a name following the pattern `prefix.<CNAME record>`. For reference, see the issue https://github.com/kubernetes-incubator/external-dns/issues/262.
|
||||
CNAMEs cannot co-exist with other records, therefore you can use the `--txt-prefix` flag which makes sure to create a TXT record with a name following the pattern `prefix.<CNAME record>`. For reference, see the issue https://github.com/kubernetes-sigs/external-dns/issues/262.
|
||||
|
||||
### Can I force ExternalDNS to create CNAME records for ELB/ALB?
|
||||
|
||||
@ -263,7 +263,7 @@ and one with `--annotation-filter=kubernetes.io/ingress.class=nginx-external`.
|
||||
|
||||
### 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-incubator/external-dns/pull/524#issue-181256561
|
||||
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
|
||||
|
||||
### How do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname`?
|
||||
|
||||
|
@ -10,7 +10,7 @@ ingress/service owns the record it can have multiple targets enable iff they are
|
||||
|
||||
## Use cases
|
||||
|
||||
See https://github.com/kubernetes-incubator/external-dns/issues/239
|
||||
See https://github.com/kubernetes-sigs/external-dns/issues/239
|
||||
|
||||
## Current behaviour
|
||||
*(as of the moment of writing)*
|
||||
@ -60,7 +60,7 @@ Once Create/Update/Delete lists are calculated correctly (this is where conflict
|
||||
`provider` specific implementation will decide how to convert the structures into required formats. If DNS provider does not (or partially) support multi targets
|
||||
then it is up to the provider to make sure that the change list of records passed to the DNS provider API is valid. **TODO**: explain best strategy.
|
||||
|
||||
Additionally see https://github.com/kubernetes-incubator/external-dns/issues/258
|
||||
Additionally see https://github.com/kubernetes-sigs/external-dns/issues/258
|
||||
|
||||
## Implementation plan
|
||||
|
||||
@ -68,19 +68,19 @@ Brief summary of open PRs and what they are trying to address:
|
||||
|
||||
### PRs
|
||||
|
||||
1. https://github.com/kubernetes-incubator/external-dns/pull/243 - first attempt to add support for multiple targets. It is lagging far behind from master tip
|
||||
1. https://github.com/kubernetes-sigs/external-dns/pull/243 - first attempt to add support for multiple targets. It is lagging far behind from master tip
|
||||
|
||||
*what it does*: unfinished attempt to extend `Endpoint` struct, for it to allow multiple targets (essentially `target string -> targets []string`)
|
||||
|
||||
*action*: evaluate if rebasing makes sense, or we can just close it.
|
||||
|
||||
2. https://github.com/kubernetes-incubator/external-dns/pull/261 - attempt to rework `plan` to make it work correctly with multiple targets.
|
||||
2. https://github.com/kubernetes-sigs/external-dns/pull/261 - attempt to rework `plan` to make it work correctly with multiple targets.
|
||||
|
||||
*what it does* : attempts to fix issues with `plan` described in `Current Behaviour` section above. Included tests reveal the current problem with `plan`
|
||||
|
||||
*action*: rebase on master and make necessary changes to satisfy requirements listed in this document including back-reference to owning record
|
||||
|
||||
3. https://github.com/kubernetes-incubator/external-dns/pull/326 - attempt to add multiple target support.
|
||||
3. https://github.com/kubernetes-sigs/external-dns/pull/326 - attempt to add multiple target support.
|
||||
|
||||
*what it does*: for each pair `DNS Name` + `Record Type` it aggregates **all** targets from the cluster and passes them to Provider. It adds basic support
|
||||
for DO, Azura, Cloudflare, AWS, GCP, however those are not tested (?). (DNSSimple and Infoblox providers were not updated)
|
||||
@ -88,8 +88,8 @@ Brief summary of open PRs and what they are trying to address:
|
||||
*action*: the `plan` logic will probably needs to be reworked, however the rest concerning support in Providers and extending `Endpoint` struct can be reused.
|
||||
Rebase on master and add missing pieces. Depends on `2`.
|
||||
|
||||
Related PRs: https://github.com/kubernetes-incubator/external-dns/pull/331/files, https://github.com/kubernetes-incubator/external-dns/pull/347/files - aiming at AWS Route53 weighted records.
|
||||
These PRs should be considered after common agreement about the way to address multi-target support is achieved. Related discussion: https://github.com/kubernetes-incubator/external-dns/issues/196
|
||||
Related PRs: https://github.com/kubernetes-sigs/external-dns/pull/331/files, https://github.com/kubernetes-sigs/external-dns/pull/347/files - aiming at AWS Route53 weighted records.
|
||||
These PRs should be considered after common agreement about the way to address multi-target support is achieved. Related discussion: https://github.com/kubernetes-sigs/external-dns/issues/196
|
||||
|
||||
### How to proceed from here
|
||||
|
||||
@ -116,4 +116,4 @@ The following steps are needed:
|
||||
## Open questions
|
||||
|
||||
- Handling cases when ingress/service targets include both hostnames and IPs - postpone this until use cases occurs
|
||||
- "Weighted records scope": https://github.com/kubernetes-incubator/external-dns/issues/196 - this should be considered once multi-target support is implemented
|
||||
- "Weighted records scope": https://github.com/kubernetes-sigs/external-dns/issues/196 - this should be considered once multi-target support is implemented
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Registry
|
||||
#### [Old name: storage]
|
||||
|
||||
Initial discussion - https://github.com/kubernetes-incubator/external-dns/issues/44
|
||||
Initial discussion - https://github.com/kubernetes-sigs/external-dns/issues/44
|
||||
|
||||
## Purpose
|
||||
|
||||
@ -102,7 +102,7 @@ It is possible that the configmap will go out of sync with the dns provider stat
|
||||
|
||||
Components:
|
||||
* Source - all endpoints ( collection of ingress, service[type=LoadBalancer] etc.)
|
||||
* [Plan](https://github.com/kubernetes-incubator/external-dns/issues/13) - object responsible for the create of change lists in external-dns
|
||||
* [Plan](https://github.com/kubernetes-sigs/external-dns/issues/13) - object responsible for the create of change lists in external-dns
|
||||
* Provider - interface to access the DNS provider API
|
||||
|
||||
Registry will serve as wrapper around `Provider` providing additional information regarding endpoint ownership. Ownership will further taken into account by `Plan` to filter out records to include only records managed by current ExternalDNS instance (having same `owner-id` value)
|
||||
|
@ -28,6 +28,7 @@ Providers
|
||||
- [ ] InMemory
|
||||
- [x] Linode
|
||||
- [x] TransIP
|
||||
- [x] RFC2136
|
||||
|
||||
PRs welcome!
|
||||
|
||||
|
@ -118,8 +118,8 @@ rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
@ -184,7 +184,7 @@ Annotations which are specific to AWS.
|
||||
|
||||
### alias
|
||||
|
||||
`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. To make the target an alias, the ingress needs to be configured correctly as described in [the docs](./nginx-ingress.md#with-a-separate-tcp-load-balancer).
|
||||
`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. To make the target an alias, the ingress needs to be configured correctly as described in [the docs](./nginx-ingress.md#with-a-separate-tcp-load-balancer). In particular, the argument `--publish-service=default/nginx-ingress-controller` has to be set on the `nginx-ingress-controller` container. If one uses the `nginx-ingress` Helm chart, this flag can be set with the `controller.publishService.enabled` configuration option.
|
||||
|
||||
## Verify ExternalDNS works (Ingress example)
|
||||
|
||||
@ -330,6 +330,23 @@ spec:
|
||||
|
||||
This will set the DNS record's TTL to 60 seconds.
|
||||
|
||||
## Routing policies
|
||||
|
||||
Route53 offers [different routing policies](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy.html). The routing policy for a record can be controlled with the following annotations:
|
||||
|
||||
* `external-dns.alpha.kubernetes.io/set-identifier`: this **needs** to be set to use any of the following routing policies
|
||||
|
||||
For any given DNS name, only **one** of the following routing policies can be used:
|
||||
|
||||
* Weighted records: `external-dns.alpha.kubernetes.io/aws-weight`
|
||||
* Latency-based routing: `external-dns.alpha.kubernetes.io/aws-region`
|
||||
* Failover:`external-dns.alpha.kubernetes.io/aws-failover`
|
||||
* Geolocation-based routing:
|
||||
* `external-dns.alpha.kubernetes.io/aws-geolocation-continent-code`
|
||||
* `external-dns.alpha.kubernetes.io/aws-geolocation-country-code`
|
||||
* `external-dns.alpha.kubernetes.io/aws-geolocation-subdivision-code`
|
||||
* Multi-value answer:`external-dns.alpha.kubernetes.io/aws-multi-value-answer`
|
||||
|
||||
## Clean up
|
||||
|
||||
Make sure to delete all Service objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
|
402
docs/tutorials/azure-private-dns.md
Normal file
402
docs/tutorials/azure-private-dns.md
Normal file
@ -0,0 +1,402 @@
|
||||
# Set up ExternalDNS for Azure Private DNS
|
||||
|
||||
This tutorial describes how to set up ExternalDNS for managing records in Azure Private DNS.
|
||||
|
||||
It comprises of the following steps:
|
||||
1) Install NGINX Ingress Controller
|
||||
2) Provision Azure Private DNS
|
||||
3) Configure service principal for managing the zone
|
||||
4) Deploy ExternalDNS
|
||||
|
||||
Everything will be deployed on Kubernetes.
|
||||
Therefore, please see the subsequent prerequisites.
|
||||
|
||||
## Prerequisites
|
||||
- Azure Kubernetes Service is deployed and ready
|
||||
- [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and `kubectl` installed on the box to execute the subsequent steps
|
||||
|
||||
## Install NGINX Ingress Controller
|
||||
|
||||
Helm is used to deploy the ingress controller.
|
||||
|
||||
We employ the popular chart [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress).
|
||||
|
||||
```
|
||||
$ helm install stable/nginx-ingress \
|
||||
--name nginx-ingress \
|
||||
--set controller.publishService.enabled=true
|
||||
```
|
||||
|
||||
The parameter `controller.publishService.enabled` needs to be set to `true.`
|
||||
|
||||
It will make the ingress controller update the endpoint records of ingress-resources to contain the external-ip of the loadbalancer serving the ingress-controller.
|
||||
This is crucial as ExternalDNS reads those endpoints records when creating DNS-Records from ingress-resources.
|
||||
In the subsequent parameter we will make use of this. If you don't want to work with ingress-resources in your later use, you can leave the parameter out.
|
||||
|
||||
Verify the correct propagation of the loadbalancer's ip by listing the ingresses.
|
||||
```
|
||||
$ kubectl get ingress
|
||||
```
|
||||
The address column should contain the ip for each ingress. ExternalDNS will pick up exactly this piece of information.
|
||||
```
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
nginx1 sample1.aks.com 52.167.195.110 80 6d22h
|
||||
nginx2 sample2.aks.com 52.167.195.110 80 6d21h
|
||||
```
|
||||
|
||||
|
||||
If you do not want to deploy the ingress controller with Helm, ensure to pass the following cmdline-flags to it through the mechanism of your choice:
|
||||
|
||||
```
|
||||
flags:
|
||||
--publish-service=<namespace of ingress-controller >/<svcname of ingress-controller>
|
||||
--update-status=true (default-value)
|
||||
|
||||
example:
|
||||
./nginx-ingress-controller --publish-service=default/nginx-ingress-controller
|
||||
```
|
||||
|
||||
## Provision Azure Private DNS
|
||||
|
||||
The provider will find suitable zones for domains it manages. It will
|
||||
not automatically create zones.
|
||||
|
||||
For this tutorial, we will create a Azure resource group named 'externaldns' that can easily be deleted later.
|
||||
|
||||
```
|
||||
$ az group create -n externaldns -l westeurope
|
||||
```
|
||||
|
||||
Substitute a more suitable location for the resource group if desired.
|
||||
|
||||
As a prerequisite for Azure Private DNS to resolve records is to define links with VNETs.
|
||||
Thus, first create a VNET.
|
||||
|
||||
```
|
||||
$ az network vnet create \
|
||||
--name myvnet \
|
||||
--resource-group externaldns \
|
||||
--location westeurope \
|
||||
--address-prefix 10.2.0.0/16 \
|
||||
--subnet-name mysubnet \
|
||||
--subnet-prefixes 10.2.0.0/24
|
||||
```
|
||||
|
||||
Next, create a Azure Private DNS zone for "example.com":
|
||||
|
||||
```
|
||||
$ az network private-dns zone create -g externaldns -n example.com
|
||||
```
|
||||
|
||||
Substitute a domain you own for "example.com" if desired.
|
||||
|
||||
Finally, create the mentioned link with the VNET.
|
||||
|
||||
```
|
||||
$ az network private-dns link vnet create -g externaldns -n mylink \
|
||||
-z example.com -v myvnet --registration-enabled false
|
||||
```
|
||||
|
||||
## Configure service principal for managing the zone
|
||||
ExternalDNS needs permissions to make changes in Azure Private DNS.
|
||||
These permissions are roles assigned to the service principal used by ExternalDNS.
|
||||
|
||||
A service principal with a minimum access level of `contributor` to the Private DNS zone(s) and `reader` to the resource group containing the Azure Private DNS zone(s) is necessary.
|
||||
More powerful role-assignments like `owner` or assignments on subscription-level work too.
|
||||
|
||||
Start off by **creating the service principal** without role-assignments.
|
||||
```
|
||||
$ az ad sp create-for-rbac --skip-assignment -n http://externaldns-sp
|
||||
{
|
||||
"appId": "appId GUID", <-- aadClientId value
|
||||
...
|
||||
"password": "password", <-- aadClientSecret value
|
||||
"tenant": "AzureAD Tenant Id" <-- tenantId value
|
||||
}
|
||||
```
|
||||
> Note: Alternatively, you can issue `az account show --query "tenantId"` to retrieve the id of your AAD Tenant too.
|
||||
|
||||
|
||||
Next, assign the roles to the service principal.
|
||||
But first **retrieve the ID's** of the objects to assign roles on.
|
||||
|
||||
```
|
||||
# find out the resource ids of the resource group where the dns zone is deployed, and the dns zone itself
|
||||
$ az group show --name externaldns --query id -o tsv
|
||||
/subscriptions/id/resourceGroups/externaldns
|
||||
|
||||
$ az network private-dns zone show --name example.com -g externaldns --query id -o tsv
|
||||
/subscriptions/.../resourceGroups/externaldns/providers/Microsoft.Network/privateDnsZones/example.com
|
||||
```
|
||||
Now, **create role assignments**.
|
||||
```
|
||||
# 1. as a reader to the resource group
|
||||
$ az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
|
||||
|
||||
# 2. as a contributor to DNS Zone itself
|
||||
$ az role assignment create --role "Contributor" --assignee <appId GUID> --scope <dns zone resource id>
|
||||
```
|
||||
|
||||
## Deploy ExternalDNS
|
||||
Configure `kubectl` to be able to communicate and authenticate with your cluster.
|
||||
This is per default done through the file `~/.kube/config`.
|
||||
|
||||
For general background information on this see [kubernetes-docs](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
|
||||
Azure-CLI features functionality for automatically maintaining this file for AKS-Clusters. See [Azure-Docs](https://docs.microsoft.com/de-de/cli/azure/aks?view=azure-cli-latest#az-aks-get-credentials).
|
||||
|
||||
Then apply one of the following manifests depending on whether you use RBAC or not.
|
||||
|
||||
The credentials of the service principal are provided to ExternalDNS as environment-variables.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: externaldns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: externaldns
|
||||
spec:
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com
|
||||
- --provider=azure-private-dns
|
||||
- --azure-resource-group=externaldns
|
||||
- --azure-subscription-id=<use the id of your subscription>
|
||||
env:
|
||||
- name: AZURE_TENANT_ID
|
||||
value: "<use the tenantId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_ID
|
||||
value: "<use the aadClientId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_SECRET
|
||||
value: "<use the aadClientSecret discovered during creation of service principal>"
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled, cluster access)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: externaldns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: externaldns
|
||||
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: ["get", "watch", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: externaldns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: externaldns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: externaldns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: externaldns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: externaldns
|
||||
spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com
|
||||
- --provider=azure-private-dns
|
||||
- --azure-resource-group=externaldns
|
||||
- --azure-subscription-id=<use the id of your subscription>
|
||||
env:
|
||||
- name: AZURE_TENANT_ID
|
||||
value: "<use the tenantId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_ID
|
||||
value: "<use the aadClientId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_SECRET
|
||||
value: "<use the aadClientSecret discovered during creation of service principal>"
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled, namespace access)
|
||||
This configuration is the same as above, except it only requires privileges for the current namespace, not for the whole cluster.
|
||||
However, access to [nodes](https://kubernetes.io/docs/concepts/architecture/nodes/) requires cluster access, so when using this manifest,
|
||||
services with type `NodePort` will be skipped!
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: externaldns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: externaldns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: externaldns
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: externaldns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: externaldns
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: externaldns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: externaldns
|
||||
spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com
|
||||
- --provider=azure-private-dns
|
||||
- --azure-resource-group=externaldns
|
||||
- --azure-subscription-id=<use the id of your subscription>
|
||||
env:
|
||||
- name: AZURE_TENANT_ID
|
||||
value: "<use the tenantId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_ID
|
||||
value: "<use the aadClientId discovered during creation of service principal>"
|
||||
- name: AZURE_CLIENT_SECRET
|
||||
value: "<use the aadClientSecret discovered during creation of service principal>"
|
||||
```
|
||||
|
||||
Create the deployment for ExternalDNS:
|
||||
|
||||
```
|
||||
$ kubectl create -f externaldns.yaml
|
||||
```
|
||||
|
||||
## Deploying sample service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-svc
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: server.example.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx-svc
|
||||
servicePort: 80
|
||||
path: /
|
||||
```
|
||||
|
||||
When using ExternalDNS with ingress objects it will automatically create DNS records based on host names specified in ingress objects that match the domain-filter argument in the externaldns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered.
|
||||
|
||||
Create the deployment, service and ingress object:
|
||||
|
||||
```
|
||||
$ kubectl create -f nginx.yaml
|
||||
```
|
||||
|
||||
Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
|
||||
|
||||
## Verify created records
|
||||
|
||||
Run the following command to view the A records for your Azure Private DNS zone:
|
||||
|
||||
```
|
||||
$ az network private-dns record-set a list -g externaldns -z example.com
|
||||
```
|
||||
|
||||
Substitute 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 ('@' indicates the record is for the zone itself).
|
@ -94,10 +94,10 @@ Create the service principal
|
||||
``` bash
|
||||
> az ad sp create-for-rbac -n ExternalDnsServicePrincipal
|
||||
{
|
||||
"appId": "appId GUID", <-- aadClientId value
|
||||
"appId": "appId GUID", --> aadClientId value
|
||||
...
|
||||
"password": "password", <-- aadClientSecret value
|
||||
"tenant": "AzureAD Tenant Id" <-- tenantId value
|
||||
"password": "password", --> aadClientSecret value
|
||||
"tenant": "AzureAD Tenant Id" --> tenantId value
|
||||
}
|
||||
```
|
||||
|
||||
@ -167,7 +167,7 @@ Ensure that your nginx-ingress deployment has the following arg: added to it:
|
||||
- --publish-service=namespace/nginx-ingress-controller-svcname
|
||||
```
|
||||
|
||||
For more details see here: [nginx-ingress external-dns](https://github.com/kubernetes-incubator/external-dns/blob/master/docs/faq.md#why-is-externaldns-only-adding-a-single-ip-address-in-route-53-on-aws-when-using-the-nginx-ingress-controller-how-do-i-get-it-to-use-the-fqdn-of-the-elb-assigned-to-my-nginx-ingress-controller-service-instead)
|
||||
For more details see here: [nginx-ingress external-dns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/faq.md#why-is-externaldns-only-adding-a-single-ip-address-in-route-53-on-aws-when-using-the-nginx-ingress-controller-how-do-i-get-it-to-use-the-fqdn-of-the-elb-assigned-to-my-nginx-ingress-controller-service-instead)
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
@ -39,6 +39,8 @@ It is important to manually create all the zones that are going to be used for k
|
||||
|
||||
Create a deployment file called `externaldns.yaml` with the following contents:
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -57,14 +59,87 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
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=designate
|
||||
env: # values from openrc file
|
||||
- name: OS_AUTH_URL
|
||||
value: http://controller/identity/v3
|
||||
value: https://controller/identity/v3
|
||||
- name: OS_REGION_NAME
|
||||
value: RegionOne
|
||||
- name: OS_USERNAME
|
||||
value: admin
|
||||
- name: OS_PASSWORD
|
||||
value: p@ssw0rd
|
||||
- name: OS_PROJECT_NAME
|
||||
value: demo
|
||||
- name: OS_USER_DOMAIN_NAME
|
||||
value: Default
|
||||
```
|
||||
|
||||
### 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"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["watch","list"]
|
||||
---
|
||||
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: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
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=designate
|
||||
env: # values from openrc file
|
||||
- name: OS_AUTH_URL
|
||||
value: https://controller/identity/v3
|
||||
- name: OS_REGION_NAME
|
||||
value: RegionOne
|
||||
- name: OS_USERNAME
|
||||
@ -83,6 +158,22 @@ Create the deployment for ExternalDNS:
|
||||
$ kubectl create -f externaldns.yaml
|
||||
```
|
||||
|
||||
### Optional: Trust self-sign certificates
|
||||
If your OpenStack-Installation is configured with a self-sign certificate, you could extend the `pod.spec` with following secret-mount:
|
||||
```yaml
|
||||
volumeMounts:
|
||||
- mountPath: /etc/ssl/certs/
|
||||
name: cacerts
|
||||
volumes:
|
||||
- name: cacerts
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: self-sign-certs
|
||||
```
|
||||
|
||||
content of the secret `self-sign-certs` must be the certificate/chain in PEM format.
|
||||
|
||||
|
||||
## Deploying an Nginx Service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
|
@ -30,6 +30,10 @@ kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
@ -95,6 +99,10 @@ kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
@ -129,6 +137,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
|
@ -100,6 +100,7 @@ spec:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Exoscale provider support was added via [this PR](https://github.com/kubernetes-incubator/external-dns/pull/625), thus you need to use external-dns v0.5.5.
|
||||
Exoscale provider support was added via [this PR](https://github.com/kubernetes-sigs/external-dns/pull/625), thus you need to use external-dns v0.5.5.
|
||||
|
||||
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.
|
||||
|
@ -29,7 +29,7 @@ spec:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --debug
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --namespace=dev
|
||||
|
@ -33,7 +33,7 @@ spec:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --debug
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --namespace=dev
|
||||
@ -101,7 +101,7 @@ spec:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:latest
|
||||
args:
|
||||
- --debug
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --namespace=dev
|
||||
|
@ -28,7 +28,6 @@ spec:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --source=istio-gateway
|
||||
- --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # load balancer service to be used; can be specified multiple times. Omit to use the default (istio-system/istio-ingressgateway)
|
||||
- --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
|
||||
@ -101,7 +100,6 @@ spec:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --source=istio-gateway
|
||||
- --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # load balancer service to be used; can be specified multiple times. Omit to use the default (istio-system/istio-ingressgateway)
|
||||
- --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
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Setting up ExternalDNS on GKE with nginx-ingress-controller
|
||||
|
||||
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/tree/master/controllers/gce) but rather uses [nginx-ingress-controller](https://github.com/kubernetes/ingress/tree/master/controllers/nginx) for that task.
|
||||
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.
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
The provider has been written for and tested against [PowerDNS](https://github.com/PowerDNS/pdns) v4.1.x and thus requires **PowerDNS Auth Server >= 4.1.x**
|
||||
|
||||
PowerDNS provider support was added via [this PR](https://github.com/kubernetes-incubator/external-dns/pull/373), thus you need to use external-dns version >= v0.5
|
||||
PowerDNS provider support was added via [this PR](https://github.com/kubernetes-sigs/external-dns/pull/373), thus you need to use external-dns version >= v0.5
|
||||
|
||||
The PDNS provider expects that your PowerDNS instance is already setup and
|
||||
functional. It expects that zones, you wish to add records to, already exist
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Setting up ExternalDNS for RancherDNS(RDNS) with kubernetes
|
||||
This tutorial describes how to setup ExternalDNS for usage within a kubernetes cluster that makes use of [RDNS](https://github.com/rancher/rdns) and [nginx ingress controller](https://github.com/kubernetes/ingress-nginx).
|
||||
This tutorial describes how to setup ExternalDNS for usage within a kubernetes cluster that makes use of [RDNS](https://github.com/rancher/rdns-server) and [nginx ingress controller](https://github.com/kubernetes/ingress-nginx).
|
||||
You need to:
|
||||
* install RDNS with [etcd](https://github.com/etcd-io/etcd) enabled
|
||||
* install external-dns with rdns as a provider
|
||||
@ -171,4 +171,4 @@ If you don't see a command prompt, try pressing enter.
|
||||
dnstools# dig @172.31.35.77 nginx.lb.rancher.cloud +short
|
||||
172.31.42.211
|
||||
dnstools#
|
||||
```
|
||||
```
|
||||
|
@ -1,69 +1,255 @@
|
||||
# Configuring RFC2136 provider
|
||||
This tutorial describes how to use the RFC2136 with either BIND or Windows DNS.
|
||||
|
||||
## Using with BIND
|
||||
To use external-dns with BIND: generate/procure a key, configure DNS and add a
|
||||
deployment of external-dns.
|
||||
|
||||
### Server credentials:
|
||||
- RFC2136 was developed for and tested with [BIND](https://www.isc.org/downloads/bind/) DNS server. This documentation assumes that you already have a configured and working server. If you don't, please check BIND documents or tutorials.
|
||||
- So you should obtain from your administrator a TSIG key. It will look like:
|
||||
- RFC2136 was developed for and tested with
|
||||
[BIND](https://www.isc.org/downloads/bind/) DNS server. This documentation
|
||||
assumes that you already have a configured and working server. If you don't,
|
||||
please check BIND documents or tutorials.
|
||||
- If your DNS is provided for you, ask for a TSIG key authorized to update and
|
||||
transfer the zone you wish to update. The key will look something like below.
|
||||
Skip the next steps wrt BIND setup.
|
||||
```text
|
||||
key "externaldns-key" {
|
||||
algorithm hmac-sha256;
|
||||
secret "XXXXXXXXXXXXXXXXXXXXXX==";
|
||||
secret "96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=";
|
||||
};
|
||||
```
|
||||
- **Warning!** Bind server configuration should enable this key for AFXR zone transfer. `external-dns` uses it for listing DNS records.
|
||||
- If you are your own DNS administrator create a TSIG key. Use
|
||||
`tsig-keygen -a hmac-sha256 externaldns` or on older distributions
|
||||
`dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST externaldns`. You will end up with
|
||||
a key printed to standard out like above (or in the case of dnssec-keygen in a
|
||||
file called `Kexternaldns......key`).
|
||||
|
||||
### BIND Configuration:
|
||||
If you do not administer your own DNS, skip to RFC provider configuration
|
||||
|
||||
- Edit your named.conf file (or appropriate included file) and add/change the
|
||||
following.
|
||||
- Make sure You are listening on the right interfaces. At least whatever
|
||||
interface external-dns will be communicating over and the interface that
|
||||
faces the internet.
|
||||
- Add the key that you generated/was given to you above. Copy paste the four
|
||||
lines that you got (not the same as the example key) into your file.
|
||||
- Create a zone for kubernetes. If you already have a zone, skip to the next
|
||||
step. (I put the zone in it's own subdirectory because named,
|
||||
which shouldn't be running as root, needs to create a journal file and the
|
||||
default zone directory isn't writeable by named).
|
||||
```text
|
||||
zone "k8s.example.org" {
|
||||
type master;
|
||||
file "/etc/bind/pri/k8s/k8s.zone";
|
||||
};
|
||||
```
|
||||
- Add your key to both transfer and update. For instance with our previous
|
||||
zone.
|
||||
```text
|
||||
zone "k8s.example.org" {
|
||||
type master;
|
||||
file "/etc/bind/pri/k8s/k8s.zone";
|
||||
allow-transfer {
|
||||
key "externaldns-key";
|
||||
};
|
||||
update-policy {
|
||||
grant externaldns-key zonesub ANY;
|
||||
};
|
||||
};
|
||||
```
|
||||
- Create a zone file (k8s.zone):
|
||||
```text
|
||||
$TTL 60 ; 1 minute
|
||||
k8s.example.org IN SOA k8s.example.org. root.k8s.example.org. (
|
||||
16 ; serial
|
||||
60 ; refresh (1 minute)
|
||||
60 ; retry (1 minute)
|
||||
60 ; expire (1 minute)
|
||||
60 ; minimum (1 minute)
|
||||
)
|
||||
NS ns.k8s.example.org.
|
||||
ns A 123.456.789.012
|
||||
```
|
||||
- Reload (or restart) named
|
||||
|
||||
|
||||
### Using external-dns
|
||||
To use external-dns add an ingress or a LoadBalancer service with a host that
|
||||
is part of the domain-filter. For example both of the following would produce
|
||||
A records.
|
||||
```text
|
||||
# cat /etc/named.conf
|
||||
...
|
||||
include "/etc/rndc.key";
|
||||
|
||||
controls {
|
||||
inet 123.123.123.123 port 953 allow { 10.x.y.151; } keys { "externaldns-key"; };
|
||||
};
|
||||
options {
|
||||
include "/etc/named/options.conf";
|
||||
};
|
||||
|
||||
include "/etc/named/zones.conf";
|
||||
...
|
||||
|
||||
# cat /etc/named/options.conf
|
||||
...
|
||||
dnssec-enable yes;
|
||||
dnssec-validation yes;
|
||||
...
|
||||
|
||||
# cat /etc/named/zones.conf
|
||||
...
|
||||
zone "example.com" {
|
||||
type master;
|
||||
file "/var/named/dynamic/db.example.com";
|
||||
update-policy {
|
||||
grant externaldns-key zonesub ANY;
|
||||
};
|
||||
};
|
||||
...
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: svc.example.org
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: ingress.example.org
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
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.
|
||||
|
||||
### Test with external-dns installed on local machine (optional)
|
||||
You may install external-dns and test on a local machine by running:
|
||||
```external-dns --txt-owner-id k8s --provider rfc2136 --rfc2136-host=192.168.0.1 --rfc2136-port=53 --rfc2136-zone=k8s.example.org --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8= --rfc2136-tsig-secret-alg=hmac-sha256 --rfc2136-tsig-keyname=externaldns-key --rfc2136-tsig-axfr --source ingress --once --domain-filter=k8s.example.org --dry-run```
|
||||
- host should be the IP of your master DNS server.
|
||||
- tsig-secret should be changed to match your secret.
|
||||
- tsig-keyname needs to match the keyname you used (if you changed it).
|
||||
- domain-filter can be used as shown to filter the domains you wish to update.
|
||||
|
||||
### RFC2136 provider configuration:
|
||||
- Example fragment of real configuration of ExternalDNS service pod.
|
||||
In order to use external-dns with your cluster you need to add a deployment
|
||||
with access to your ingress and service resources. The following are two
|
||||
example manifests with and without RBAC respectively.
|
||||
|
||||
- With RBAC:
|
||||
```text
|
||||
...
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: external-dns
|
||||
labels:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- list
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
namespace: external-dns
|
||||
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:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:v0.5.17
|
||||
args:
|
||||
- --txt-owner-id=k8s
|
||||
- --provider=rfc2136
|
||||
- --rfc2136-host=123.123.123.123
|
||||
- --rfc2136-host=192.168.0.1
|
||||
- --rfc2136-port=53
|
||||
- --rfc2136-zone=your-domain.com
|
||||
- --rfc2136-tsig-secret=${rfc2136_tsig_secret}
|
||||
- --rfc2136-zone=k8s.example.org
|
||||
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
|
||||
- --rfc2136-tsig-secret-alg=hmac-sha256
|
||||
- --rfc2136-tsig-keyname=externaldns-key
|
||||
- --rfc2136-tsig-axfr
|
||||
...
|
||||
- --source=ingress
|
||||
- --domain-filter=k8s.example.org
|
||||
```
|
||||
- `--rfc2136-tsig-secret` - environment variable containing actual secret value from TSIG key. Something like `XXXXXXXXXXXXXXXXXXXXXX==`.
|
||||
- `--rfc2136-tsig-keyname` - this is a string parameter with the key name in the Kubernetes secret. It **must match** with key name on the DNS server. In this example it is `externaldns-key`.
|
||||
|
||||
## Using with Microsoft DNS
|
||||
|
||||
- Without RBAC:
|
||||
```text
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: external-dns
|
||||
labels:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:v0.5.17
|
||||
args:
|
||||
- --txt-owner-id=k8s
|
||||
- --provider=rfc2136
|
||||
- --rfc2136-host=192.168.0.1
|
||||
- --rfc2136-port=53
|
||||
- --rfc2136-zone=k8s.example.org
|
||||
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
|
||||
- --rfc2136-tsig-secret-alg=hmac-sha256
|
||||
- --rfc2136-tsig-keyname=externaldns-key
|
||||
- --rfc2136-tsig-axfr
|
||||
- --source=ingress
|
||||
- --domain-filter=k8s.example.org
|
||||
```
|
||||
|
||||
## Microsoft DNS
|
||||
|
||||
While `external-dns` was not developed or tested against Microsoft DNS, it can be configured to work against it. YMMV.
|
||||
|
||||
|
@ -42,6 +42,10 @@ nginx 10.0.0.115 34.x.x.x 80:30543/TCP 2m
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
**Note for examples below**
|
||||
|
||||
When using `registry=txt` option, make sure to also use the `txt-prefix` and `txt-owner-id` options as well. If you try to create a `TXT` record in VinylDNS without a prefix, it will try to create a `TXT` record with the same name as your actual DNS record and fail (creating a stranded record `external-dns` cannot manage).
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
@ -67,6 +71,9 @@ spec:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --registry=txt
|
||||
- --txt-owner-id=grizz
|
||||
- --txt-prefix=txt-
|
||||
env:
|
||||
- name: VINYLDNS_HOST
|
||||
value: "YOUR_VINYLDNS_HOST"
|
||||
@ -138,6 +145,9 @@ spec:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --registry=txt
|
||||
- --txt-owner-id=grizz
|
||||
- --txt-prefix=txt-
|
||||
env:
|
||||
env:
|
||||
- name: VINYLDNS_HOST
|
||||
@ -167,7 +177,7 @@ export VINYLDNS_SECRET_KEY=<secret key>
|
||||
--domain-filter=elements.capsps.comcast.net. \
|
||||
--zone-id-filter=20e8bfd2-3a70-4e1b-8e11-c9c1948528d3 \
|
||||
--registry=txt \
|
||||
--txt-owner-id=grizz- \
|
||||
--txt-owner-id=grizz \
|
||||
--txt-prefix=txt- \
|
||||
--namespace=default \
|
||||
--once \
|
||||
|
@ -126,6 +126,8 @@ type Endpoint struct {
|
||||
Targets Targets `json:"targets,omitempty"`
|
||||
// RecordType type of record, e.g. CNAME, A, SRV, TXT etc
|
||||
RecordType string `json:"recordType,omitempty"`
|
||||
// Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple')
|
||||
SetIdentifier string `json:"setIdentifier,omitempty"`
|
||||
// TTL for the record
|
||||
RecordTTL TTL `json:"recordTTL,omitempty"`
|
||||
// Labels stores labels defined for the Endpoint
|
||||
@ -157,6 +159,11 @@ func NewEndpointWithTTL(dnsName, recordType string, ttl TTL, targets ...string)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Endpoint) WithSetIdentifier(setIdentifier string) *Endpoint {
|
||||
e.SetIdentifier = setIdentifier
|
||||
return e
|
||||
}
|
||||
|
||||
// WithProviderSpecific attaches a key/value pair to the Endpoint and returns the Endpoint.
|
||||
// This can be used to pass additional data through the stages of ExternalDNS's Endpoint processing.
|
||||
// The assumption is that most of the time this will be provider specific metadata that doesn't
|
||||
@ -182,7 +189,7 @@ func (e *Endpoint) GetProviderSpecificProperty(key string) (ProviderSpecificProp
|
||||
}
|
||||
|
||||
func (e *Endpoint) String() string {
|
||||
return fmt.Sprintf("%s %d IN %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Targets, e.ProviderSpecific)
|
||||
return fmt.Sprintf("%s %d IN %s %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.SetIdentifier, e.Targets, e.ProviderSpecific)
|
||||
}
|
||||
|
||||
// DNSEndpointSpec defines the desired state of DNSEndpoint
|
||||
|
87
go.mod
87
go.mod
@ -1,11 +1,14 @@
|
||||
module github.com/kubernetes-incubator/external-dns
|
||||
module sigs.k8s.io/external-dns
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.37.4
|
||||
github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible
|
||||
github.com/Azure/go-autorest v10.9.0+incompatible
|
||||
cloud.google.com/go v0.44.3
|
||||
github.com/Azure/azure-sdk-for-go v36.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.9.0
|
||||
github.com/Azure/go-autorest/autorest/adal v0.6.0
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.0.0-00010101000000-000000000000
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect
|
||||
github.com/alecthomas/kingpin v2.2.5+incompatible
|
||||
@ -15,13 +18,9 @@ require (
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.10.1
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/coreos/bbolt v1.3.2 // indirect
|
||||
github.com/coreos/etcd v3.3.10+incompatible
|
||||
github.com/coreos/go-semver v0.2.0 // indirect
|
||||
github.com/coreos/etcd v3.3.15+incompatible
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/digitalocean/godo v1.19.0
|
||||
github.com/dnaeon/go-vcr v1.0.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.14.0
|
||||
@ -29,68 +28,52 @@ require (
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
||||
github.com/go-resty/resty v1.8.0 // indirect
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b // indirect
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
|
||||
github.com/hashicorp/go-multierror v1.0.0 // indirect
|
||||
github.com/heptio/contour v0.13.0
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/gophercloud/gophercloud v0.1.0
|
||||
github.com/heptio/contour v0.15.0
|
||||
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.6 // indirect
|
||||
github.com/linki/instrumented_http v0.2.0
|
||||
github.com/linode/linodego v0.3.0
|
||||
github.com/mattn/go-isatty v0.0.7 // indirect
|
||||
github.com/miekg/dns v1.0.8
|
||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
||||
github.com/miekg/dns v1.0.14
|
||||
github.com/nesv/go-dynect v0.6.0
|
||||
github.com/nic-at/rc0go v1.1.0
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/oracle/oci-go-sdk v1.8.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829
|
||||
github.com/prometheus/client_golang v0.9.3
|
||||
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/sergi/go-diff v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
|
||||
github.com/smartystreets/gunit v1.0.2 // indirect
|
||||
github.com/soheilhy/cmux v0.1.3 // indirect
|
||||
github.com/spf13/cobra v0.0.3 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 // indirect
|
||||
github.com/transip/gotransip v5.8.2+incompatible
|
||||
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 // indirect
|
||||
go.etcd.io/bbolt v1.3.2 // indirect
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1 // indirect
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a
|
||||
google.golang.org/api v0.3.1
|
||||
google.golang.org/appengine v1.5.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
google.golang.org/api v0.9.0
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
istio.io/api v0.0.0-20190321180614-db16d82d3672
|
||||
istio.io/api v0.0.0-20190820204432-483f2547d882
|
||||
istio.io/istio v0.0.0-20190322063008-2b1331886076
|
||||
k8s.io/api v0.0.0-20190503184017-f1b257a4ce96
|
||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190503184539-c338b28ceaa1 // indirect
|
||||
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841
|
||||
k8s.io/client-go v8.0.0+incompatible
|
||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719
|
||||
k8s.io/client-go v10.0.0+incompatible
|
||||
k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c // indirect
|
||||
)
|
||||
|
||||
replace k8s.io/code-generator v0.0.0-20190409092313-b1289fc74931 => k8s.io/code-generator v0.0.0-20181128191024-b1289fc74931
|
||||
|
||||
replace github.com/golang/glog => github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d
|
||||
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
|
||||
istio.io/api => istio.io/api v0.0.0-20190820204432-483f2547d882
|
||||
istio.io/istio => istio.io/istio v0.0.0-20190911205955-c2bd59595ce6
|
||||
k8s.io/api => k8s.io/api v0.0.0-20190817221950-ebce17126a01
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190919022157-e8460a76b3ad
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190817221809-bf4de9df677c
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190817224438-0337ccdab819
|
||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20190817222206-ee6c071a42cf
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b
|
||||
)
|
||||
|
562
go.sum
562
go.sum
@ -1,18 +1,63 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.3 h1:0sMegbmn/8uTwpNkB0q9cLEpZ2W5a6kl+wtBQgPWBJQ=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/logging v1.0.0/go.mod h1:V1cc3ogwobYzQq5f2R7DS/GvRIrI4FKj01Gs5glwAls=
|
||||
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=
|
||||
github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBgN43vA3Bb0fAlCBIMm9avXbcHlE=
|
||||
github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+GC1eNuHZeRQYQxKkk=
|
||||
github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.6.0/go.mod h1:QeFzMJDAw8TXt5+aRaSuE8l5BwaMIOIlaVkBOPRuMuw=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc=
|
||||
fortio.org/fortio v1.3.0/go.mod h1:Go0fRqoPJ1xy5JOWcS23jyF58byVZxFyEePYsGmCR0k=
|
||||
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=
|
||||
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/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/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/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
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/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v0.0.0-20190301161902-9f8fceff796f/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig v2.14.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo=
|
||||
github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
|
||||
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/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
@ -27,138 +72,273 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5Vpd
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/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/antlr/antlr4 v0.0.0-20190223165740-dade65a895c2/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.13.24/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=
|
||||
github.com/aws/aws-sdk-go v1.23.18 h1:ADU/y1EO8yPzUJJYjcvJ0V9/suezxPh0u6hb5bSYIGQ=
|
||||
github.com/aws/aws-sdk-go v1.23.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
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 v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
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/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.10.1 h1:d2CL6F9k2O0Ux0w27LgogJ5UOzZRj6a/hDPFqPP68d8=
|
||||
github.com/cloudflare/cloudflare-go v0.10.1/go.mod h1:C0Y6eWnTJPMK2ceuOxx2pjh78UUHihcXeTTHb8r7QjU=
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
|
||||
github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE=
|
||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK4aZY8jz+DAF0FHjI51BXjjSwCzS1Dk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/prometheus-operator v0.29.0/go.mod h1:SO+r5yZUacDFPKHfPoUjI3hMsH+ZUdiuNNhuSq3WoSg=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||
github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/dchest/siphash v1.1.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5 h1:YjnQWGUNtqeKqndapy9V1BzlfMwc/dBJf2MU9dmuXSQ=
|
||||
github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/digitalocean/godo v1.19.0 h1:9ApuchfzGD/XI8Zm0RRnZnytdfYHPjPTRKTnmzQNV7o=
|
||||
github.com/digitalocean/godo v1.19.0/go.mod h1:AAPQ+tiM4st79QHlEBTg8LM7JQNre4SAQCbn56wEyKY=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnsimple/dnsimple-go v0.14.0 h1:JGYtVVA/uHc91q0LjDWqR1oVj6EGu9Kn0lMRxjH/w30=
|
||||
github.com/dnsimple/dnsimple-go v0.14.0/go.mod h1:0FYu4qVNv/UcfZPNwa9zi68IkggJu3TIwM54D7rhmI4=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dropbox/godropbox v0.0.0-20190501155911-5749d3b71cbe/go.mod h1:glr97hP/JuXb+WMYCizc4PIFuzw1lCR97mwbe1VVXhQ=
|
||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
|
||||
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/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk=
|
||||
github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/elazarl/goproxy v0.0.0-20190630181448-f1e96bc0f4c5/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190630181448-f1e96bc0f4c5/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful v2.9.3+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.8.7-0.20190821215049-f062b07a671a h1:SaBXBWjRmig9yVB49C6TcbDtbZTbhuFLod7YiGjuFxQ=
|
||||
github.com/envoyproxy/go-control-plane v0.8.7-0.20190821215049-f062b07a671a/go.mod h1:XB9+ce7x+IrsjgIVnRnql0O61gj/np0/bGDfhJI3sCU=
|
||||
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 v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/stackerr v0.0.0-20150612192056-c2fcf88613f4/go.mod h1:SBHk9aNQtiw4R4bEuzHjVmZikkUKCnO1v3lPQ21HZGk=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
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/fluent/fluent-logger-golang v1.3.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU=
|
||||
github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
|
||||
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/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-ini/ini v1.33.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-resty/resty v1.8.0 h1:vbNCxbHOWCototzwxf3L63PQCKx6xgT6v8SHfoqkp6U=
|
||||
github.com/go-resty/resty v1.8.0/go.mod h1:n37daLLGIHq2FFYHxg+FYQiwA95FpfNI+A9uxoIYGRk=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gocql/gocql v0.0.0-20190423091413-b99afaf3b163/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
||||
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 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48 h1:X+zN6RZXsvnrSJaAIQhZezPfAfvsqihKKR8oiLHid34=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
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/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/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
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-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/cel-go v0.2.0/go.mod h1:fTCVOuSN/Vn6d49zvRpr3fDAKFyfpLViE0gU+9Vtm7g=
|
||||
github.com/google/cel-spec v0.2.0/go.mod h1:MjQm800JAGhOZXI7vatnVpmIaFTR6L8FHcKk+piiKpI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
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-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190424031112-b9b92a825806/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
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 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20171214222146-0e7658f8ee99/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/consul v1.3.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.9.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-memdb v1.0.1/go.mod h1:I6dKdmYhZqU0RJSheVEWgTNWdVQH5QvTgIUQ0t/t32M=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/go-plugin v1.0.0/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
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/heptio/contour v0.13.0 h1:kiJ7qC439vFD21DTyQPNPzOGfDqkO/y//GrLYWJ5MdY=
|
||||
github.com/heptio/contour v0.13.0/go.mod h1:qYE0FAuA8W1NJEaHmyOoWkJZTRPL1x4GVO5BGZMG1Os=
|
||||
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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
|
||||
github.com/hashicorp/vault v0.10.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/heptio/contour v0.15.0 h1:JhyJMauwoYtqTzd23jStx7Bx7S3sHbqTWZs6+Ed+UPE=
|
||||
github.com/heptio/contour v0.15.0/go.mod h1:y4LmuX+86v8mlRd1HVrb2u4t77jMjOQ3DnjfRCiwrfA=
|
||||
github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
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/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo8vFvoQ=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
@ -167,15 +347,23 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jteeuwen/go-bindata v0.0.0-20180305030458-6025e8de665b/go.mod h1:JVvhzYOiGBnFSYRyV00iY8q7/0PThjIYav1p9h5dmKs=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/errors v0.0.0-20190207033735-e65537c515d7/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20190212223446-d976af380377/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/testing v0.0.0-20190429233213-dfc56b8c09fc/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/keybase/go-crypto v0.0.0-20190416182011-b785b22cc757/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@ -184,10 +372,17 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46OtdhH2vVt8mJ1EWUE94k99vbN9fZs1WQ8kcEapU=
|
||||
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
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.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY=
|
||||
github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY=
|
||||
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-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw=
|
||||
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
@ -196,13 +391,24 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdempsky/unconvert v0.0.0-20190325185700-2f5dc3378ed3/go.mod h1:9+3Wp2ccIz73BJqVfc7n2+1A+mzvnEwtDTqEjeRngBQ=
|
||||
github.com/miekg/dns v1.0.8 h1:Zi8HNpze3NeRWH1PQV6O71YcvJRQ6j0lORO6DAEmAAI=
|
||||
github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@ -212,6 +418,9 @@ github.com/nesv/go-dynect v0.6.0 h1:Ow/DiSm4LAISwnFku/FITSQHnU6pBvhQMsUE5Gu6Oq4=
|
||||
github.com/nesv/go-dynect v0.6.0/go.mod h1:GHRBRKzTwjAMhosHJQq/KrZaFkXIFyJ5zRE7thGXXrs=
|
||||
github.com/nic-at/rc0go v1.1.0 h1:k6/Bru/npTjmCSFw65ulYRw/b3ycIS30t6/YM4r42V4=
|
||||
github.com/nic-at/rc0go v1.1.0/go.mod h1:KEa3H5fmDNXCaXSqOeAZxkKnG/8ggr1OHIG25Ve7fjU=
|
||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
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=
|
||||
@ -220,50 +429,83 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/open-policy-agent/opa v0.8.2/go.mod h1:rlfeSeHuZmMEpmrcGla42AjkOUjP4rGIpS96H12un3o=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/openshift/api v0.0.0-20190322043348-8741ff068a47/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/operator-framework/operator-sdk v0.7.0/go.mod h1:iVyukRkam5JZa8AnjYf+/G3rk7JI1+M6GsU0sq0B9NA=
|
||||
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/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
|
||||
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
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 h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pierrec/lz4 v2.2.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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
|
||||
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 v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
|
||||
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/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
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.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
|
||||
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 h1:0aNv3xC7DmQoy1/x1sMh18g+fihWW68LL13i8ao9kl4=
|
||||
github.com/prometheus/procfs v0.0.0-20190403104016-ea9eea638872/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.2.1/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/ryanuber/go-glob v0.0.0-20160226084822-572520ed46db/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
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/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sethgrid/pester v0.0.0-20180227223404-ed9870dad317/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/signalfx/com_signalfx_metrics_protobuf v0.0.0-20170330202426-93e507b42f43/go.mod h1:muYA2clvwCdj7nzAJ5vJIXYpJsUumhAl4Uu1wUNpWzA=
|
||||
github.com/signalfx/gohistogram v0.0.0-20160107210732-1ccfd2ff5083/go.mod h1:adPDS6s7WaajdFBV9mQ7i0dKfQ8xiDnF9ZNETVPpp7c=
|
||||
github.com/signalfx/golib v1.1.6/go.mod h1:nWYefOwlUKWm/SpN/LgVSBnyH1T9NpT1ANlmgRIi1Cs=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
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=
|
||||
@ -274,101 +516,147 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/gunit v1.0.2 h1:nCXf7TXGchqk8zNVQFTtbeW6rGSrlp9ir9h8wmx0xws=
|
||||
github.com/smartystreets/gunit v1.0.2/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||
github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
|
||||
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=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
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/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/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 v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs=
|
||||
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI=
|
||||
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/uber/jaeger-client-go v0.0.0-20190228190846-ecf2d03a9e80/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
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/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92 h1:Q76MzqJu++vAfhj0mVf7t0F4xHUbg+V/d/Uk5PBQjRU=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92/go.mod h1:AZuEfReFWdvtU0LatbLpo70t3lqdLvph2D5mqFP0bkA=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180206001645-7af743e8ec84/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
|
||||
github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 h1:lkoOjizoHqOcEFsvYGE5c8Ykdijjnd0R3r1yDYHzLno=
|
||||
github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268/go.mod h1:mq0zhomp/G6rRTb0dvHWXRHr/2+Qgeq5hMXfJ670+i4=
|
||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.20.1 h1:pMEjRZ1M4ebWGikflH7nQpV6+Zr88KBMA2XJD3sbijw=
|
||||
github.com/yuin/gopher-lua v0.0.0-20180316054350-84ea3a3c79b3/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
|
||||
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-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190404164418-38d8ce5564a5 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/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-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/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 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c h1:hDn6jm7snBX2O7+EeTk6Q4WXJfKt7MWgtiCCRi1rBoY=
|
||||
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/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/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-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
|
||||
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=
|
||||
@ -377,75 +665,129 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190213192042-740235f6c0d8/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
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 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/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-20190822000311-fc82fb2afd64/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190227213309-4f5b463f9597/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
|
||||
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=
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
|
||||
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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/logfmt.v0 v0.3.0/go.mod h1:mRLMcMLrml5h2Ux/H+4zccFOlVCiRvOvndsolsJoU8Q=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 h1:+fgY/3ngqdBW9oLQCMwL5g+QRkKFPJH05fx2/pipqRQ=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
|
||||
gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek=
|
||||
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/stack.v1 v1.7.0/go.mod h1:QtWz4C5wbvhA63ngux3942W/ppRxtyYjHvvhz02s7+M=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
istio.io/api v0.0.0-20190321180614-db16d82d3672 h1:luY97pBVarSo1v++zf2kgb84Q55G5hv/ult2A4KPQuk=
|
||||
istio.io/api v0.0.0-20190321180614-db16d82d3672/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo=
|
||||
istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
|
||||
istio.io/istio v0.0.0-20190322063008-2b1331886076 h1:gZhCrmVzfQJoDl4oav8i5+NF7p7v0M1Pou+2O+hZBtc=
|
||||
istio.io/istio v0.0.0-20190322063008-2b1331886076/go.mod h1:OWBySrQjjk549IhxWCt7DTl9ZSsXdvbgm+SmgGVRsGA=
|
||||
k8s.io/api v0.0.0-20190226173710-145d52631d00 h1:xYfyMq0qxTGAg3O9GK23GMbNrBcpnFg9IeA6isDgIXk=
|
||||
k8s.io/api v0.0.0-20190226173710-145d52631d00/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/api v0.0.0-20190503184017-f1b257a4ce96 h1:zq/7PZXqJ6ZbPfLRbIm9Qs6gHMviY72SPk4ugPUPDvI=
|
||||
k8s.io/api v0.0.0-20190503184017-f1b257a4ce96/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190503184539-c338b28ceaa1 h1:uNrdMFGXgDAaw+WyJSuRhnzW2eZkqZjc04SZOr4wky8=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190503184539-c338b28ceaa1/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
||||
k8s.io/apimachinery v0.0.0-20190221084156-01f179d85dbc h1:7z9/6jKWBqkK9GI1RRB0B5fZcmkatLQ/nv8kysch24o=
|
||||
k8s.io/apimachinery v0.0.0-20190221084156-01f179d85dbc/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841 h1:Q4RZrHNtlC/mSdC1sTrcZ5RchC/9vxLVj57pWiCBKv4=
|
||||
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/client-go v0.0.0-20190226174127-78295b709ec6/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4=
|
||||
k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/code-generator v0.0.0-20181128191024-b1289fc74931/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
|
||||
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=
|
||||
istio.io/api v0.0.0-20190820204432-483f2547d882 h1:L0WC/5HTk8T5eGTg/ka9jGZgw7GMuWj9rm6DFF4owL8=
|
||||
istio.io/api v0.0.0-20190820204432-483f2547d882/go.mod h1:42cBjnu/rTJcCaKi8nLdIvq0n71RcLrkgZ9IQSvDdSQ=
|
||||
istio.io/gogo-genproto v0.0.0-20190614210408-e88dc8b0e4db/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
|
||||
istio.io/gogo-genproto v0.0.0-20190731221249-06e20ada0df2/go.mod h1:IjvrbUlRbbw4JCpsgvgihcz9USUwEoNTL/uwMtyV5yk=
|
||||
istio.io/gogo-genproto v0.0.0-20190819131816-7a8328e41c1a h1:QN+P7SPcjI4az1Lb40MdhJg/OkV03u5XS1DVIk8PS6E=
|
||||
istio.io/gogo-genproto v0.0.0-20190819131816-7a8328e41c1a/go.mod h1:IjvrbUlRbbw4JCpsgvgihcz9USUwEoNTL/uwMtyV5yk=
|
||||
istio.io/istio v0.0.0-20190911205955-c2bd59595ce6 h1:SICsYFkNnMRHPPxHYUGMqZp1lQXW8+nJKBSIZVIk16M=
|
||||
istio.io/istio v0.0.0-20190911205955-c2bd59595ce6/go.mod h1:dn7qqBbC/pAcJnqNvB2jv7llDBbExQVbnPIVuPPScx8=
|
||||
istio.io/operator v0.0.0-20190830172131-647a5416137b/go.mod h1:NW/+IXU4IeeVtWLS8S57Vq67H/GOy92Temo4eI5efLs=
|
||||
istio.io/pkg v0.0.0-20190515193414-9332430ad747/go.mod h1:0EkPwmR0tESYjN4Ilq1D52nTBurXaQvny3r2VY4j4tw=
|
||||
istio.io/pkg v0.0.0-20190731230704-fcbac27d69d5 h1:HcASpvj/fuuABkYH9YbsTGEOT75YHyWvvFnTe229zXs=
|
||||
istio.io/pkg v0.0.0-20190731230704-fcbac27d69d5/go.mod h1:We4ZQuCbiiNfXge2GfOshBsyDXVwzFwP8703V5DcM14=
|
||||
k8s.io/api v0.0.0-20190817221950-ebce17126a01 h1:AMUY6ojynTCBURCALg9KVOsrCmWjHF7h7UbuBod7FYc=
|
||||
k8s.io/api v0.0.0-20190817221950-ebce17126a01/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190919022157-e8460a76b3ad h1:I3kcGO+4TazAR49NWgNiEGND6b4q5HVc7Q0K1IQxV9Q=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190919022157-e8460a76b3ad/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
||||
k8s.io/apimachinery v0.0.0-20190817221809-bf4de9df677c h1:TlFvXut3q9hNMA2UdjrC6LLfANYrh4lnDzb0gDNSMSg=
|
||||
k8s.io/apimachinery v0.0.0-20190817221809-bf4de9df677c/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/apiserver v0.0.0-20181213151703-3ccfe8365421/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
|
||||
k8s.io/cli-runtime v0.0.0-20190817224438-0337ccdab819/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM=
|
||||
k8s.io/client-go v0.0.0-20190817222206-ee6c071a42cf h1:ZCiWjWPoxHYlMo7N4ZPz7yqo2YuCPvBi3nNX11oBTMg=
|
||||
k8s.io/client-go v0.0.0-20190817222206-ee6c071a42cf/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
|
||||
k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/kube-openapi v0.0.0-20190115222348-ced9eb3070a5/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/helm v2.13.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE=
|
||||
k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
mvdan.cc/unparam v0.0.0-20190310220240-1b9ccfa71afe/go.mod h1:BnhuWBAqxH3+J5bDybdxgw5ZfS+DsVd4iylsKQePN8o=
|
||||
k8s.io/kubernetes v1.13.1/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
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.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
|
||||
sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
/** test utility functions for endpoints verifications */
|
||||
@ -47,10 +47,10 @@ func (b byAllFields) Less(i, j int) bool {
|
||||
// SameEndpoint returns true if two endpoints are same
|
||||
// considers example.org. and example.org DNSName/Target as different endpoints
|
||||
func SameEndpoint(a, b *endpoint.Endpoint) bool {
|
||||
return a.DNSName == b.DNSName && a.Targets.Same(b.Targets) && a.RecordType == b.RecordType &&
|
||||
return a.DNSName == b.DNSName && a.Targets.Same(b.Targets) && a.RecordType == b.RecordType && a.SetIdentifier == b.SetIdentifier &&
|
||||
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL &&
|
||||
a.Labels[endpoint.ResourceLabelKey] == b.Labels[endpoint.ResourceLabelKey] &&
|
||||
SameProverSpecific(a.ProviderSpecific, b.ProviderSpecific)
|
||||
SameProviderSpecific(a.ProviderSpecific, b.ProviderSpecific)
|
||||
}
|
||||
|
||||
// SameEndpoints compares two slices of endpoints regardless of order
|
||||
@ -76,6 +76,7 @@ func SameEndpoints(a, b []*endpoint.Endpoint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SameEndpointLabels verifies that labels of the two slices of endpoints are the same
|
||||
func SameEndpointLabels(a, b []*endpoint.Endpoint) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
@ -100,7 +101,7 @@ func SamePlanChanges(a, b map[string][]*endpoint.Endpoint) bool {
|
||||
SameEndpoints(a["UpdateOld"], b["UpdateOld"]) && SameEndpoints(a["UpdateNew"], b["UpdateNew"])
|
||||
}
|
||||
|
||||
// SameProverSpecific verifies that two maps contain the same string/string key/value pairs
|
||||
func SameProverSpecific(a, b endpoint.ProviderSpecific) bool {
|
||||
// SameProviderSpecific verifies that two maps contain the same string/string key/value pairs
|
||||
func SameProviderSpecific(a, b endpoint.ProviderSpecific) bool {
|
||||
return reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func ExampleSameEndpoints() {
|
||||
@ -40,9 +40,10 @@ func ExampleSameEndpoints() {
|
||||
RecordType: endpoint.RecordTypeTXT,
|
||||
},
|
||||
{
|
||||
DNSName: "abc.com",
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
DNSName: "abc.com",
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
SetIdentifier: "test-set-1",
|
||||
},
|
||||
{
|
||||
DNSName: "bbc.com",
|
||||
@ -68,11 +69,11 @@ func ExampleSameEndpoints() {
|
||||
fmt.Println(ep)
|
||||
}
|
||||
// Output:
|
||||
// abc.com 0 IN A 1.2.3.4 []
|
||||
// abc.com 0 IN TXT something []
|
||||
// bbc.com 0 IN CNAME foo.com []
|
||||
// cbc.com 60 IN CNAME foo.com []
|
||||
// example.org 0 IN load-balancer.org []
|
||||
// example.org 0 IN load-balancer.org [{foo bar}]
|
||||
// example.org 0 IN TXT load-balancer.org []
|
||||
// abc.com 0 IN A test-set-1 1.2.3.4 []
|
||||
// abc.com 0 IN TXT something []
|
||||
// bbc.com 0 IN CNAME foo.com []
|
||||
// cbc.com 60 IN CNAME foo.com []
|
||||
// example.org 0 IN load-balancer.org []
|
||||
// example.org 0 IN load-balancer.org [{foo bar}]
|
||||
// example.org 0 IN TXT load-balancer.org []
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package testutils
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// MockSource returns mock endpoints.
|
||||
|
22
main.go
22
main.go
@ -27,13 +27,13 @@ import (
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/controller"
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns/validation"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
"github.com/kubernetes-incubator/external-dns/registry"
|
||||
"github.com/kubernetes-incubator/external-dns/source"
|
||||
"sigs.k8s.io/external-dns/controller"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
"sigs.k8s.io/external-dns/registry"
|
||||
"sigs.k8s.io/external-dns/source"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -133,8 +133,10 @@ func main() {
|
||||
cfg.Registry = "aws-sd"
|
||||
}
|
||||
p, err = provider.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun)
|
||||
case "azure":
|
||||
p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
|
||||
case "azure-dns", "azure":
|
||||
p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||
case "azure-private-dns":
|
||||
p, err = provider.NewAzurePrivateDNSProvider(domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureSubscriptionID, cfg.DryRun)
|
||||
case "vinyldns":
|
||||
p, err = provider.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
||||
case "cloudflare":
|
||||
@ -142,7 +144,7 @@ func main() {
|
||||
case "rcodezero":
|
||||
p, err = provider.NewRcodeZeroProvider(domainFilter, cfg.DryRun, cfg.RcodezeroTXTEncrypt)
|
||||
case "google":
|
||||
p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
|
||||
p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.DryRun)
|
||||
case "digitalocean":
|
||||
p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
|
||||
case "linode":
|
||||
|
@ -37,94 +37,98 @@ var (
|
||||
|
||||
// Config is a project-wide configuration
|
||||
type Config struct {
|
||||
Master string
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
IstioIngressGatewayServices []string
|
||||
ContourLoadBalancerService string
|
||||
Sources []string
|
||||
Namespace string
|
||||
AnnotationFilter string
|
||||
FQDNTemplate string
|
||||
CombineFQDNAndAnnotation bool
|
||||
IgnoreHostnameAnnotation bool
|
||||
Compatibility string
|
||||
PublishInternal bool
|
||||
PublishHostIP bool
|
||||
ConnectorSourceServer string
|
||||
Provider string
|
||||
GoogleProject string
|
||||
DomainFilter []string
|
||||
ExcludeDomains []string
|
||||
ZoneIDFilter []string
|
||||
AlibabaCloudConfigFile string
|
||||
AlibabaCloudZoneType string
|
||||
AWSZoneType string
|
||||
AWSZoneTagFilter []string
|
||||
AWSAssumeRole string
|
||||
AWSBatchChangeSize int
|
||||
AWSBatchChangeInterval time.Duration
|
||||
AWSEvaluateTargetHealth bool
|
||||
AWSAPIRetries int
|
||||
AWSPreferCNAME bool
|
||||
AzureConfigFile string
|
||||
AzureResourceGroup string
|
||||
CloudflareProxied bool
|
||||
CloudflareZonesPerPage int
|
||||
CoreDNSPrefix string
|
||||
RcodezeroTXTEncrypt bool
|
||||
InfobloxGridHost string
|
||||
InfobloxWapiPort int
|
||||
InfobloxWapiUsername string
|
||||
InfobloxWapiPassword string `secure:"yes"`
|
||||
InfobloxWapiVersion string
|
||||
InfobloxSSLVerify bool
|
||||
InfobloxView string
|
||||
InfobloxMaxResults int
|
||||
DynCustomerName string
|
||||
DynUsername string
|
||||
DynPassword string `secure:"yes"`
|
||||
DynMinTTLSeconds int
|
||||
OCIConfigFile string
|
||||
InMemoryZones []string
|
||||
PDNSServer string
|
||||
PDNSAPIKey string `secure:"yes"`
|
||||
PDNSTLSEnabled bool
|
||||
TLSCA string
|
||||
TLSClientCert string
|
||||
TLSClientCertKey string
|
||||
Policy string
|
||||
Registry string
|
||||
TXTOwnerID string
|
||||
TXTPrefix string
|
||||
Interval time.Duration
|
||||
Once bool
|
||||
DryRun bool
|
||||
LogFormat string
|
||||
MetricsAddress string
|
||||
LogLevel string
|
||||
TXTCacheInterval time.Duration
|
||||
ExoscaleEndpoint string
|
||||
ExoscaleAPIKey string `secure:"yes"`
|
||||
ExoscaleAPISecret string `secure:"yes"`
|
||||
CRDSourceAPIVersion string
|
||||
CRDSourceKind string
|
||||
ServiceTypeFilter []string
|
||||
CFAPIEndpoint string
|
||||
CFUsername string
|
||||
CFPassword string
|
||||
RFC2136Host string
|
||||
RFC2136Port int
|
||||
RFC2136Zone string
|
||||
RFC2136Insecure bool
|
||||
RFC2136TSIGKeyName string
|
||||
RFC2136TSIGSecret string `secure:"yes"`
|
||||
RFC2136TSIGSecretAlg string
|
||||
RFC2136TAXFR bool
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
TransIPAccountName string
|
||||
TransIPPrivateKeyFile string
|
||||
Master string
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
IstioIngressGatewayServices []string
|
||||
ContourLoadBalancerService string
|
||||
Sources []string
|
||||
Namespace string
|
||||
AnnotationFilter string
|
||||
FQDNTemplate string
|
||||
CombineFQDNAndAnnotation bool
|
||||
IgnoreHostnameAnnotation bool
|
||||
Compatibility string
|
||||
PublishInternal bool
|
||||
PublishHostIP bool
|
||||
ConnectorSourceServer string
|
||||
Provider string
|
||||
GoogleProject string
|
||||
GoogleBatchChangeSize int
|
||||
GoogleBatchChangeInterval time.Duration
|
||||
DomainFilter []string
|
||||
ExcludeDomains []string
|
||||
ZoneIDFilter []string
|
||||
AlibabaCloudConfigFile string
|
||||
AlibabaCloudZoneType string
|
||||
AWSZoneType string
|
||||
AWSZoneTagFilter []string
|
||||
AWSAssumeRole string
|
||||
AWSBatchChangeSize int
|
||||
AWSBatchChangeInterval time.Duration
|
||||
AWSEvaluateTargetHealth bool
|
||||
AWSAPIRetries int
|
||||
AWSPreferCNAME bool
|
||||
AzureConfigFile string
|
||||
AzureResourceGroup string
|
||||
AzureSubscriptionID string
|
||||
AzureUserAssignedIdentityClientID string
|
||||
CloudflareProxied bool
|
||||
CloudflareZonesPerPage int
|
||||
CoreDNSPrefix string
|
||||
RcodezeroTXTEncrypt bool
|
||||
InfobloxGridHost string
|
||||
InfobloxWapiPort int
|
||||
InfobloxWapiUsername string
|
||||
InfobloxWapiPassword string `secure:"yes"`
|
||||
InfobloxWapiVersion string
|
||||
InfobloxSSLVerify bool
|
||||
InfobloxView string
|
||||
InfobloxMaxResults int
|
||||
DynCustomerName string
|
||||
DynUsername string
|
||||
DynPassword string `secure:"yes"`
|
||||
DynMinTTLSeconds int
|
||||
OCIConfigFile string
|
||||
InMemoryZones []string
|
||||
PDNSServer string
|
||||
PDNSAPIKey string `secure:"yes"`
|
||||
PDNSTLSEnabled bool
|
||||
TLSCA string
|
||||
TLSClientCert string
|
||||
TLSClientCertKey string
|
||||
Policy string
|
||||
Registry string
|
||||
TXTOwnerID string
|
||||
TXTPrefix string
|
||||
Interval time.Duration
|
||||
Once bool
|
||||
DryRun bool
|
||||
LogFormat string
|
||||
MetricsAddress string
|
||||
LogLevel string
|
||||
TXTCacheInterval time.Duration
|
||||
ExoscaleEndpoint string
|
||||
ExoscaleAPIKey string `secure:"yes"`
|
||||
ExoscaleAPISecret string `secure:"yes"`
|
||||
CRDSourceAPIVersion string
|
||||
CRDSourceKind string
|
||||
ServiceTypeFilter []string
|
||||
CFAPIEndpoint string
|
||||
CFUsername string
|
||||
CFPassword string
|
||||
RFC2136Host string
|
||||
RFC2136Port int
|
||||
RFC2136Zone string
|
||||
RFC2136Insecure bool
|
||||
RFC2136TSIGKeyName string
|
||||
RFC2136TSIGSecret string `secure:"yes"`
|
||||
RFC2136TSIGSecretAlg string
|
||||
RFC2136TAXFR bool
|
||||
NS1Endpoint string
|
||||
NS1IgnoreSSL bool
|
||||
TransIPAccountName string
|
||||
TransIPPrivateKeyFile string
|
||||
}
|
||||
|
||||
var defaultConfig = &Config{
|
||||
@ -145,6 +149,8 @@ var defaultConfig = &Config{
|
||||
ConnectorSourceServer: "localhost:8080",
|
||||
Provider: "",
|
||||
GoogleProject: "",
|
||||
GoogleBatchChangeSize: 1000,
|
||||
GoogleBatchChangeInterval: time.Second,
|
||||
DomainFilter: []string{},
|
||||
ExcludeDomains: []string{},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
@ -158,6 +164,7 @@ var defaultConfig = &Config{
|
||||
AWSPreferCNAME: false,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
@ -258,9 +265,6 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig)
|
||||
app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout)
|
||||
|
||||
// Flags related to Istio
|
||||
app.Flag("istio-ingress-gateway", "The fully-qualified name of the Istio ingress gateway service. Flag can be specified multiple times (default: istio-system/istio-ingressgateway)").Default("istio-system/istio-ingressgateway").StringsVar(&cfg.IstioIngressGatewayServices)
|
||||
|
||||
// Flags related to cloud foundry
|
||||
app.Flag("cf-api-endpoint", "The fully-qualified domain name of the cloud foundry instance you are targeting").Default(defaultConfig.CFAPIEndpoint).StringVar(&cfg.CFAPIEndpoint)
|
||||
app.Flag("cf-username", "The username to log into the cloud foundry API").Default(defaultConfig.CFUsername).StringVar(&cfg.CFUsername)
|
||||
@ -270,7 +274,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("contour-load-balancer", "The fully-qualified name of the Contour load balancer service. (default: heptio-contour/contour)").Default("heptio-contour/contour").StringVar(&cfg.ContourLoadBalancerService)
|
||||
|
||||
// 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, fake, connector, istio-gateway, cloudfoundry, contour-ingressroute, crd, empty").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "cloudfoundry", "contour-ingressroute", "fake", "connector", "crd", "empty")
|
||||
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, cloudfoundry, contour-ingressroute, crd, empty)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "cloudfoundry", "contour-ingressroute", "fake", "connector", "crd", "empty")
|
||||
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("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)
|
||||
@ -285,11 +289,13 @@ 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, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
|
||||
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, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
|
||||
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)
|
||||
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)
|
||||
app.Flag("google-batch-change-interval", "When using the Google provider, set the interval between batch changes.").Default(defaultConfig.GoogleBatchChangeInterval.String()).DurationVar(&cfg.GoogleBatchChangeInterval)
|
||||
app.Flag("alibaba-cloud-config-file", "When using the Alibaba Cloud provider, specify the Alibaba Cloud configuration file (required when --provider=alibabacloud").Default(defaultConfig.AlibabaCloudConfigFile).StringVar(&cfg.AlibabaCloudConfigFile)
|
||||
app.Flag("alibaba-cloud-zone-type", "When using the Alibaba Cloud provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AlibabaCloudZoneType).EnumVar(&cfg.AlibabaCloudZoneType, "", "public", "private")
|
||||
app.Flag("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private")
|
||||
@ -301,7 +307,9 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
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("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 (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
||||
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)
|
||||
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
|
||||
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
|
||||
app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage)
|
||||
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
||||
|
@ -29,197 +29,140 @@ import (
|
||||
|
||||
var (
|
||||
minimalConfig = &Config{
|
||||
Master: "",
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"},
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
Sources: []string{"service"},
|
||||
Namespace: "",
|
||||
FQDNTemplate: "",
|
||||
Compatibility: "",
|
||||
Provider: "google",
|
||||
GoogleProject: "",
|
||||
DomainFilter: []string{""},
|
||||
ExcludeDomains: []string{""},
|
||||
ZoneIDFilter: []string{""},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "",
|
||||
AWSZoneTagFilter: []string{""},
|
||||
AWSAssumeRole: "",
|
||||
AWSBatchChangeSize: 1000,
|
||||
AWSBatchChangeInterval: time.Second,
|
||||
AWSEvaluateTargetHealth: true,
|
||||
AWSAPIRetries: 3,
|
||||
AWSPreferCNAME: false,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
InfobloxGridHost: "",
|
||||
InfobloxWapiPort: 443,
|
||||
InfobloxWapiUsername: "admin",
|
||||
InfobloxWapiPassword: "",
|
||||
InfobloxWapiVersion: "2.3.1",
|
||||
InfobloxView: "",
|
||||
InfobloxSSLVerify: true,
|
||||
InfobloxMaxResults: 0,
|
||||
OCIConfigFile: "/etc/kubernetes/oci.yaml",
|
||||
InMemoryZones: []string{""},
|
||||
PDNSServer: "http://localhost:8081",
|
||||
PDNSAPIKey: "",
|
||||
Policy: "sync",
|
||||
Registry: "txt",
|
||||
TXTOwnerID: "default",
|
||||
TXTPrefix: "",
|
||||
TXTCacheInterval: 0,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
DryRun: false,
|
||||
LogFormat: "text",
|
||||
MetricsAddress: ":7979",
|
||||
LogLevel: logrus.InfoLevel.String(),
|
||||
ConnectorSourceServer: "localhost:8080",
|
||||
ExoscaleEndpoint: "https://api.exoscale.ch/dns",
|
||||
ExoscaleAPIKey: "",
|
||||
ExoscaleAPISecret: "",
|
||||
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
|
||||
CRDSourceKind: "DNSEndpoint",
|
||||
RcodezeroTXTEncrypt: false,
|
||||
TransIPAccountName: "",
|
||||
TransIPPrivateKeyFile: "",
|
||||
Master: "",
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
Sources: []string{"service"},
|
||||
Namespace: "",
|
||||
FQDNTemplate: "",
|
||||
Compatibility: "",
|
||||
Provider: "google",
|
||||
GoogleProject: "",
|
||||
GoogleBatchChangeSize: 1000,
|
||||
GoogleBatchChangeInterval: time.Second,
|
||||
DomainFilter: []string{""},
|
||||
ExcludeDomains: []string{""},
|
||||
ZoneIDFilter: []string{""},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "",
|
||||
AWSZoneTagFilter: []string{""},
|
||||
AWSAssumeRole: "",
|
||||
AWSBatchChangeSize: 1000,
|
||||
AWSBatchChangeInterval: time.Second,
|
||||
AWSEvaluateTargetHealth: true,
|
||||
AWSAPIRetries: 3,
|
||||
AWSPreferCNAME: false,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
InfobloxGridHost: "",
|
||||
InfobloxWapiPort: 443,
|
||||
InfobloxWapiUsername: "admin",
|
||||
InfobloxWapiPassword: "",
|
||||
InfobloxWapiVersion: "2.3.1",
|
||||
InfobloxView: "",
|
||||
InfobloxSSLVerify: true,
|
||||
InfobloxMaxResults: 0,
|
||||
OCIConfigFile: "/etc/kubernetes/oci.yaml",
|
||||
InMemoryZones: []string{""},
|
||||
PDNSServer: "http://localhost:8081",
|
||||
PDNSAPIKey: "",
|
||||
Policy: "sync",
|
||||
Registry: "txt",
|
||||
TXTOwnerID: "default",
|
||||
TXTPrefix: "",
|
||||
TXTCacheInterval: 0,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
DryRun: false,
|
||||
LogFormat: "text",
|
||||
MetricsAddress: ":7979",
|
||||
LogLevel: logrus.InfoLevel.String(),
|
||||
ConnectorSourceServer: "localhost:8080",
|
||||
ExoscaleEndpoint: "https://api.exoscale.ch/dns",
|
||||
ExoscaleAPIKey: "",
|
||||
ExoscaleAPISecret: "",
|
||||
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
|
||||
CRDSourceKind: "DNSEndpoint",
|
||||
RcodezeroTXTEncrypt: false,
|
||||
TransIPAccountName: "",
|
||||
TransIPPrivateKeyFile: "",
|
||||
}
|
||||
|
||||
overriddenConfig = &Config{
|
||||
Master: "http://127.0.0.1:8080",
|
||||
KubeConfig: "/some/path",
|
||||
RequestTimeout: time.Second * 77,
|
||||
IstioIngressGatewayServices: []string{"istio-other/istio-otheringressgateway"},
|
||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||
Sources: []string{"service", "ingress", "connector"},
|
||||
Namespace: "namespace",
|
||||
IgnoreHostnameAnnotation: true,
|
||||
FQDNTemplate: "{{.Name}}.service.example.com",
|
||||
Compatibility: "mate",
|
||||
Provider: "google",
|
||||
GoogleProject: "project",
|
||||
DomainFilter: []string{"example.org", "company.com"},
|
||||
ExcludeDomains: []string{"xapi.example.org", "xapi.company.com"},
|
||||
ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "private",
|
||||
AWSZoneTagFilter: []string{"tag=foo"},
|
||||
AWSAssumeRole: "some-other-role",
|
||||
AWSBatchChangeSize: 100,
|
||||
AWSBatchChangeInterval: time.Second * 2,
|
||||
AWSEvaluateTargetHealth: false,
|
||||
AWSAPIRetries: 13,
|
||||
AWSPreferCNAME: true,
|
||||
AzureConfigFile: "azure.json",
|
||||
AzureResourceGroup: "arg",
|
||||
CloudflareProxied: true,
|
||||
CloudflareZonesPerPage: 20,
|
||||
CoreDNSPrefix: "/coredns/",
|
||||
InfobloxGridHost: "127.0.0.1",
|
||||
InfobloxWapiPort: 8443,
|
||||
InfobloxWapiUsername: "infoblox",
|
||||
InfobloxWapiPassword: "infoblox",
|
||||
InfobloxWapiVersion: "2.6.1",
|
||||
InfobloxView: "internal",
|
||||
InfobloxSSLVerify: false,
|
||||
InfobloxMaxResults: 2000,
|
||||
OCIConfigFile: "oci.yaml",
|
||||
InMemoryZones: []string{"example.org", "company.com"},
|
||||
PDNSServer: "http://ns.example.com:8081",
|
||||
PDNSAPIKey: "some-secret-key",
|
||||
PDNSTLSEnabled: true,
|
||||
TLSCA: "/path/to/ca.crt",
|
||||
TLSClientCert: "/path/to/cert.pem",
|
||||
TLSClientCertKey: "/path/to/key.pem",
|
||||
Policy: "upsert-only",
|
||||
Registry: "noop",
|
||||
TXTOwnerID: "owner-1",
|
||||
TXTPrefix: "associated-txt-record",
|
||||
TXTCacheInterval: 12 * time.Hour,
|
||||
Interval: 10 * time.Minute,
|
||||
Once: true,
|
||||
DryRun: true,
|
||||
LogFormat: "json",
|
||||
MetricsAddress: "127.0.0.1:9099",
|
||||
LogLevel: logrus.DebugLevel.String(),
|
||||
ConnectorSourceServer: "localhost:8081",
|
||||
ExoscaleEndpoint: "https://api.foo.ch/dns",
|
||||
ExoscaleAPIKey: "1",
|
||||
ExoscaleAPISecret: "2",
|
||||
CRDSourceAPIVersion: "test.k8s.io/v1alpha1",
|
||||
CRDSourceKind: "Endpoint",
|
||||
RcodezeroTXTEncrypt: true,
|
||||
NS1Endpoint: "https://api.example.com/v1",
|
||||
NS1IgnoreSSL: true,
|
||||
TransIPAccountName: "transip",
|
||||
TransIPPrivateKeyFile: "/path/to/transip.key",
|
||||
}
|
||||
|
||||
// minimal config with istio gateway source and multiple ingressgateway load balancer services
|
||||
multipleIstioIngressGatewaysConfig = &Config{
|
||||
Master: "",
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway", "istio-other/istio-otheringressgateway"},
|
||||
Sources: []string{"istio-gateway"},
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
Namespace: "",
|
||||
FQDNTemplate: "",
|
||||
Compatibility: "",
|
||||
Provider: "google",
|
||||
GoogleProject: "",
|
||||
DomainFilter: []string{""},
|
||||
ExcludeDomains: []string{""},
|
||||
ZoneIDFilter: []string{""},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "",
|
||||
AWSZoneTagFilter: []string{""},
|
||||
AWSAssumeRole: "",
|
||||
AWSBatchChangeSize: 1000,
|
||||
AWSBatchChangeInterval: time.Second,
|
||||
AWSEvaluateTargetHealth: true,
|
||||
AWSAPIRetries: 3,
|
||||
AWSPreferCNAME: false,
|
||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||
AzureResourceGroup: "",
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
InfobloxGridHost: "",
|
||||
InfobloxWapiPort: 443,
|
||||
InfobloxWapiUsername: "admin",
|
||||
InfobloxWapiPassword: "",
|
||||
InfobloxWapiVersion: "2.3.1",
|
||||
InfobloxView: "",
|
||||
InfobloxSSLVerify: true,
|
||||
OCIConfigFile: "/etc/kubernetes/oci.yaml",
|
||||
InMemoryZones: []string{""},
|
||||
PDNSServer: "http://localhost:8081",
|
||||
PDNSAPIKey: "",
|
||||
Policy: "sync",
|
||||
Registry: "txt",
|
||||
TXTOwnerID: "default",
|
||||
TXTPrefix: "",
|
||||
TXTCacheInterval: 0,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
DryRun: false,
|
||||
LogFormat: "text",
|
||||
MetricsAddress: ":7979",
|
||||
LogLevel: logrus.InfoLevel.String(),
|
||||
ConnectorSourceServer: "localhost:8080",
|
||||
ExoscaleEndpoint: "https://api.exoscale.ch/dns",
|
||||
ExoscaleAPIKey: "",
|
||||
ExoscaleAPISecret: "",
|
||||
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
|
||||
CRDSourceKind: "DNSEndpoint",
|
||||
RcodezeroTXTEncrypt: false,
|
||||
Master: "http://127.0.0.1:8080",
|
||||
KubeConfig: "/some/path",
|
||||
RequestTimeout: time.Second * 77,
|
||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||
Sources: []string{"service", "ingress", "connector"},
|
||||
Namespace: "namespace",
|
||||
IgnoreHostnameAnnotation: true,
|
||||
FQDNTemplate: "{{.Name}}.service.example.com",
|
||||
Compatibility: "mate",
|
||||
Provider: "google",
|
||||
GoogleProject: "project",
|
||||
GoogleBatchChangeSize: 100,
|
||||
GoogleBatchChangeInterval: time.Second * 2,
|
||||
DomainFilter: []string{"example.org", "company.com"},
|
||||
ExcludeDomains: []string{"xapi.example.org", "xapi.company.com"},
|
||||
ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "private",
|
||||
AWSZoneTagFilter: []string{"tag=foo"},
|
||||
AWSAssumeRole: "some-other-role",
|
||||
AWSBatchChangeSize: 100,
|
||||
AWSBatchChangeInterval: time.Second * 2,
|
||||
AWSEvaluateTargetHealth: false,
|
||||
AWSAPIRetries: 13,
|
||||
AWSPreferCNAME: true,
|
||||
AzureConfigFile: "azure.json",
|
||||
AzureResourceGroup: "arg",
|
||||
AzureSubscriptionID: "arg",
|
||||
CloudflareProxied: true,
|
||||
CloudflareZonesPerPage: 20,
|
||||
CoreDNSPrefix: "/coredns/",
|
||||
InfobloxGridHost: "127.0.0.1",
|
||||
InfobloxWapiPort: 8443,
|
||||
InfobloxWapiUsername: "infoblox",
|
||||
InfobloxWapiPassword: "infoblox",
|
||||
InfobloxWapiVersion: "2.6.1",
|
||||
InfobloxView: "internal",
|
||||
InfobloxSSLVerify: false,
|
||||
InfobloxMaxResults: 2000,
|
||||
OCIConfigFile: "oci.yaml",
|
||||
InMemoryZones: []string{"example.org", "company.com"},
|
||||
PDNSServer: "http://ns.example.com:8081",
|
||||
PDNSAPIKey: "some-secret-key",
|
||||
PDNSTLSEnabled: true,
|
||||
TLSCA: "/path/to/ca.crt",
|
||||
TLSClientCert: "/path/to/cert.pem",
|
||||
TLSClientCertKey: "/path/to/key.pem",
|
||||
Policy: "upsert-only",
|
||||
Registry: "noop",
|
||||
TXTOwnerID: "owner-1",
|
||||
TXTPrefix: "associated-txt-record",
|
||||
TXTCacheInterval: 12 * time.Hour,
|
||||
Interval: 10 * time.Minute,
|
||||
Once: true,
|
||||
DryRun: true,
|
||||
LogFormat: "json",
|
||||
MetricsAddress: "127.0.0.1:9099",
|
||||
LogLevel: logrus.DebugLevel.String(),
|
||||
ConnectorSourceServer: "localhost:8081",
|
||||
ExoscaleEndpoint: "https://api.foo.ch/dns",
|
||||
ExoscaleAPIKey: "1",
|
||||
ExoscaleAPISecret: "2",
|
||||
CRDSourceAPIVersion: "test.k8s.io/v1alpha1",
|
||||
CRDSourceKind: "Endpoint",
|
||||
RcodezeroTXTEncrypt: true,
|
||||
NS1Endpoint: "https://api.example.com/v1",
|
||||
NS1IgnoreSSL: true,
|
||||
TransIPAccountName: "transip",
|
||||
TransIPPrivateKeyFile: "/path/to/transip.key",
|
||||
}
|
||||
)
|
||||
|
||||
@ -245,7 +188,6 @@ func TestParseFlags(t *testing.T) {
|
||||
"--master=http://127.0.0.1:8080",
|
||||
"--kubeconfig=/some/path",
|
||||
"--request-timeout=77s",
|
||||
"--istio-ingress-gateway=istio-other/istio-otheringressgateway",
|
||||
"--contour-load-balancer=heptio-contour-other/contour-other",
|
||||
"--source=service",
|
||||
"--source=ingress",
|
||||
@ -256,8 +198,11 @@ func TestParseFlags(t *testing.T) {
|
||||
"--compatibility=mate",
|
||||
"--provider=google",
|
||||
"--google-project=project",
|
||||
"--google-batch-change-size=100",
|
||||
"--google-batch-change-interval=2s",
|
||||
"--azure-config-file=azure.json",
|
||||
"--azure-resource-group=arg",
|
||||
"--azure-subscription-id=arg",
|
||||
"--cloudflare-proxied",
|
||||
"--cloudflare-zones-per-page=20",
|
||||
"--coredns-prefix=/coredns/",
|
||||
@ -322,97 +267,78 @@ func TestParseFlags(t *testing.T) {
|
||||
title: "override everything via environment variables",
|
||||
args: []string{},
|
||||
envVars: map[string]string{
|
||||
"EXTERNAL_DNS_MASTER": "http://127.0.0.1:8080",
|
||||
"EXTERNAL_DNS_KUBECONFIG": "/some/path",
|
||||
"EXTERNAL_DNS_REQUEST_TIMEOUT": "77s",
|
||||
"EXTERNAL_DNS_ISTIO_INGRESS_GATEWAY": "istio-other/istio-otheringressgateway",
|
||||
"EXTERNAL_DNS_CONTOUR_LOAD_BALANCER": "heptio-contour-other/contour-other",
|
||||
"EXTERNAL_DNS_SOURCE": "service\ningress\nconnector",
|
||||
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
||||
"EXTERNAL_DNS_FQDN_TEMPLATE": "{{.Name}}.service.example.com",
|
||||
"EXTERNAL_DNS_IGNORE_HOSTNAME_ANNOTATION": "1",
|
||||
"EXTERNAL_DNS_COMPATIBILITY": "mate",
|
||||
"EXTERNAL_DNS_PROVIDER": "google",
|
||||
"EXTERNAL_DNS_GOOGLE_PROJECT": "project",
|
||||
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
||||
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
||||
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
||||
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
||||
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
||||
"EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD": "infoblox",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_VERSION": "2.6.1",
|
||||
"EXTERNAL_DNS_INFOBLOX_VIEW": "internal",
|
||||
"EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0",
|
||||
"EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000",
|
||||
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
|
||||
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
|
||||
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
|
||||
"EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com",
|
||||
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
|
||||
"EXTERNAL_DNS_PDNS_API_KEY": "some-secret-key",
|
||||
"EXTERNAL_DNS_PDNS_TLS_ENABLED": "1",
|
||||
"EXTERNAL_DNS_RDNS_ROOT_DOMAIN": "lb.rancher.cloud",
|
||||
"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_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TYPE": "private",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TAGS": "tag=foo",
|
||||
"EXTERNAL_DNS_AWS_ASSUME_ROLE": "some-other-role",
|
||||
"EXTERNAL_DNS_AWS_BATCH_CHANGE_SIZE": "100",
|
||||
"EXTERNAL_DNS_AWS_BATCH_CHANGE_INTERVAL": "2s",
|
||||
"EXTERNAL_DNS_AWS_EVALUATE_TARGET_HEALTH": "0",
|
||||
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
|
||||
"EXTERNAL_DNS_AWS_PREFER_CNAME": "true",
|
||||
"EXTERNAL_DNS_POLICY": "upsert-only",
|
||||
"EXTERNAL_DNS_REGISTRY": "noop",
|
||||
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
|
||||
"EXTERNAL_DNS_TXT_PREFIX": "associated-txt-record",
|
||||
"EXTERNAL_DNS_TXT_CACHE_INTERVAL": "12h",
|
||||
"EXTERNAL_DNS_INTERVAL": "10m",
|
||||
"EXTERNAL_DNS_ONCE": "1",
|
||||
"EXTERNAL_DNS_DRY_RUN": "1",
|
||||
"EXTERNAL_DNS_LOG_FORMAT": "json",
|
||||
"EXTERNAL_DNS_METRICS_ADDRESS": "127.0.0.1:9099",
|
||||
"EXTERNAL_DNS_LOG_LEVEL": "debug",
|
||||
"EXTERNAL_DNS_CONNECTOR_SOURCE_SERVER": "localhost:8081",
|
||||
"EXTERNAL_DNS_EXOSCALE_ENDPOINT": "https://api.foo.ch/dns",
|
||||
"EXTERNAL_DNS_EXOSCALE_APIKEY": "1",
|
||||
"EXTERNAL_DNS_EXOSCALE_APISECRET": "2",
|
||||
"EXTERNAL_DNS_CRD_SOURCE_APIVERSION": "test.k8s.io/v1alpha1",
|
||||
"EXTERNAL_DNS_CRD_SOURCE_KIND": "Endpoint",
|
||||
"EXTERNAL_DNS_RCODEZERO_TXT_ENCRYPT": "1",
|
||||
"EXTERNAL_DNS_NS1_ENDPOINT": "https://api.example.com/v1",
|
||||
"EXTERNAL_DNS_NS1_IGNORESSL": "1",
|
||||
"EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip",
|
||||
"EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key",
|
||||
"EXTERNAL_DNS_MASTER": "http://127.0.0.1:8080",
|
||||
"EXTERNAL_DNS_KUBECONFIG": "/some/path",
|
||||
"EXTERNAL_DNS_REQUEST_TIMEOUT": "77s",
|
||||
"EXTERNAL_DNS_CONTOUR_LOAD_BALANCER": "heptio-contour-other/contour-other",
|
||||
"EXTERNAL_DNS_SOURCE": "service\ningress\nconnector",
|
||||
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
||||
"EXTERNAL_DNS_FQDN_TEMPLATE": "{{.Name}}.service.example.com",
|
||||
"EXTERNAL_DNS_IGNORE_HOSTNAME_ANNOTATION": "1",
|
||||
"EXTERNAL_DNS_COMPATIBILITY": "mate",
|
||||
"EXTERNAL_DNS_PROVIDER": "google",
|
||||
"EXTERNAL_DNS_GOOGLE_PROJECT": "project",
|
||||
"EXTERNAL_DNS_GOOGLE_BATCH_CHANGE_SIZE": "100",
|
||||
"EXTERNAL_DNS_GOOGLE_BATCH_CHANGE_INTERVAL": "2s",
|
||||
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
||||
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
||||
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
||||
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
||||
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
||||
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
||||
"EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD": "infoblox",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_VERSION": "2.6.1",
|
||||
"EXTERNAL_DNS_INFOBLOX_VIEW": "internal",
|
||||
"EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0",
|
||||
"EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000",
|
||||
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
|
||||
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
|
||||
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
|
||||
"EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com",
|
||||
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
|
||||
"EXTERNAL_DNS_PDNS_API_KEY": "some-secret-key",
|
||||
"EXTERNAL_DNS_PDNS_TLS_ENABLED": "1",
|
||||
"EXTERNAL_DNS_RDNS_ROOT_DOMAIN": "lb.rancher.cloud",
|
||||
"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_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TYPE": "private",
|
||||
"EXTERNAL_DNS_AWS_ZONE_TAGS": "tag=foo",
|
||||
"EXTERNAL_DNS_AWS_ASSUME_ROLE": "some-other-role",
|
||||
"EXTERNAL_DNS_AWS_BATCH_CHANGE_SIZE": "100",
|
||||
"EXTERNAL_DNS_AWS_BATCH_CHANGE_INTERVAL": "2s",
|
||||
"EXTERNAL_DNS_AWS_EVALUATE_TARGET_HEALTH": "0",
|
||||
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
|
||||
"EXTERNAL_DNS_AWS_PREFER_CNAME": "true",
|
||||
"EXTERNAL_DNS_POLICY": "upsert-only",
|
||||
"EXTERNAL_DNS_REGISTRY": "noop",
|
||||
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
|
||||
"EXTERNAL_DNS_TXT_PREFIX": "associated-txt-record",
|
||||
"EXTERNAL_DNS_TXT_CACHE_INTERVAL": "12h",
|
||||
"EXTERNAL_DNS_INTERVAL": "10m",
|
||||
"EXTERNAL_DNS_ONCE": "1",
|
||||
"EXTERNAL_DNS_DRY_RUN": "1",
|
||||
"EXTERNAL_DNS_LOG_FORMAT": "json",
|
||||
"EXTERNAL_DNS_METRICS_ADDRESS": "127.0.0.1:9099",
|
||||
"EXTERNAL_DNS_LOG_LEVEL": "debug",
|
||||
"EXTERNAL_DNS_CONNECTOR_SOURCE_SERVER": "localhost:8081",
|
||||
"EXTERNAL_DNS_EXOSCALE_ENDPOINT": "https://api.foo.ch/dns",
|
||||
"EXTERNAL_DNS_EXOSCALE_APIKEY": "1",
|
||||
"EXTERNAL_DNS_EXOSCALE_APISECRET": "2",
|
||||
"EXTERNAL_DNS_CRD_SOURCE_APIVERSION": "test.k8s.io/v1alpha1",
|
||||
"EXTERNAL_DNS_CRD_SOURCE_KIND": "Endpoint",
|
||||
"EXTERNAL_DNS_RCODEZERO_TXT_ENCRYPT": "1",
|
||||
"EXTERNAL_DNS_NS1_ENDPOINT": "https://api.example.com/v1",
|
||||
"EXTERNAL_DNS_NS1_IGNORESSL": "1",
|
||||
"EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip",
|
||||
"EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key",
|
||||
},
|
||||
expected: overriddenConfig,
|
||||
},
|
||||
{
|
||||
title: "istio config with 2 ingressgateways",
|
||||
args: []string{
|
||||
"--provider=google",
|
||||
"--source=istio-gateway",
|
||||
"--istio-ingress-gateway=istio-system/istio-ingressgateway",
|
||||
"--istio-ingress-gateway=istio-other/istio-otheringressgateway",
|
||||
},
|
||||
envVars: map[string]string{},
|
||||
expected: multipleIstioIngressGatewaysConfig,
|
||||
},
|
||||
{
|
||||
title: "override everything via environment variables with multiple istio ingress gateway load balancer services",
|
||||
args: []string{},
|
||||
envVars: map[string]string{
|
||||
"EXTERNAL_DNS_PROVIDER": "google",
|
||||
"EXTERNAL_DNS_SOURCE": "istio-gateway",
|
||||
"EXTERNAL_DNS_ISTIO_INGRESS_GATEWAY": "istio-system/istio-ingressgateway\nistio-other/istio-otheringressgateway",
|
||||
},
|
||||
expected: multipleIstioIngressGatewaysConfig,
|
||||
},
|
||||
} {
|
||||
t.Run(ti.title, func(t *testing.T) {
|
||||
originalEnv := setEnv(t, ti.envVars)
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
)
|
||||
|
||||
// ValidateConfig performs validation on the Config object
|
||||
|
@ -19,7 +19,7 @@ package validation
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -19,7 +19,7 @@ package plan
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// ConflictResolver is used to make a decision in case of two or more different kubernetes resources
|
||||
|
@ -19,8 +19,9 @@ package plan
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
var _ ConflictResolver = PerResource{}
|
||||
|
108
plan/plan.go
108
plan/plan.go
@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// Plan can convert a list of desired and current records to a series of create,
|
||||
@ -63,12 +63,12 @@ bar.com | | [->191.1.1.1, ->190.1.1.1] | = create (bar.com -> 1
|
||||
"=", i.e. result of calculation relies on supplied ConflictResolver
|
||||
*/
|
||||
type planTable struct {
|
||||
rows map[string]*planTableRow
|
||||
rows map[string]map[string]*planTableRow
|
||||
resolver ConflictResolver
|
||||
}
|
||||
|
||||
func newPlanTable() planTable { //TODO: make resolver configurable
|
||||
return planTable{map[string]*planTableRow{}, PerResource{}}
|
||||
return planTable{map[string]map[string]*planTableRow{}, PerResource{}}
|
||||
}
|
||||
|
||||
// planTableRow
|
||||
@ -86,52 +86,23 @@ func (t planTableRow) String() string {
|
||||
func (t planTable) addCurrent(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = &planTableRow{}
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
}
|
||||
t.rows[dnsName].current = e
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier].current = e
|
||||
}
|
||||
|
||||
func (t planTable) addCandidate(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = &planTableRow{}
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
}
|
||||
t.rows[dnsName].candidates = append(t.rows[dnsName].candidates, e)
|
||||
}
|
||||
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
func (t planTable) getUpdates() (updateNew []*endpoint.Endpoint, updateOld []*endpoint.Endpoint) {
|
||||
for _, row := range t.rows {
|
||||
if row.current != nil && len(row.candidates) > 0 { //dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
updateNew = append(updateNew, update)
|
||||
updateOld = append(updateOld, row.current)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t planTable) getCreates() (createList []*endpoint.Endpoint) {
|
||||
for _, row := range t.rows {
|
||||
if row.current == nil { //dns name not taken
|
||||
createList = append(createList, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t planTable) getDeletes() (deleteList []*endpoint.Endpoint) {
|
||||
for _, row := range t.rows {
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
deleteList = append(deleteList, row.current)
|
||||
}
|
||||
}
|
||||
return
|
||||
t.rows[dnsName][e.SetIdentifier].candidates = append(t.rows[dnsName][e.SetIdentifier].candidates, e)
|
||||
}
|
||||
|
||||
// Calculate computes the actions needed to move current state towards desired
|
||||
@ -148,9 +119,29 @@ func (p *Plan) Calculate() *Plan {
|
||||
}
|
||||
|
||||
changes := &Changes{}
|
||||
changes.Create = t.getCreates()
|
||||
changes.Delete = t.getDeletes()
|
||||
changes.UpdateNew, changes.UpdateOld = t.getUpdates()
|
||||
|
||||
for _, topRow := range t.rows {
|
||||
for _, row := range topRow {
|
||||
if row.current == nil { //dns name not taken
|
||||
changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
changes.Delete = append(changes.Delete, row.current)
|
||||
}
|
||||
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
if row.current != nil && len(row.candidates) > 0 { //dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
changes.UpdateNew = append(changes.UpdateNew, update)
|
||||
changes.UpdateOld = append(changes.UpdateOld, row.current)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, pol := range p.Policies {
|
||||
changes = pol.Apply(changes)
|
||||
}
|
||||
@ -191,16 +182,39 @@ func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool {
|
||||
}
|
||||
for _, c := range current.ProviderSpecific {
|
||||
// don't consider target health when detecting changes
|
||||
// see: https://github.com/kubernetes-incubator/external-dns/issues/869#issuecomment-458576954
|
||||
// see: https://github.com/kubernetes-sigs/external-dns/issues/869#issuecomment-458576954
|
||||
if c.Name == "aws/evaluate-target-health" {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, d := range desired.ProviderSpecific {
|
||||
if d.Name == c.Name && d.Value != c.Value {
|
||||
return true
|
||||
if d.Name == c.Name {
|
||||
if d.Value != c.Value {
|
||||
// provider-specific attribute updated
|
||||
return true
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// provider-specific attribute deleted
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, d := range desired.ProviderSpecific {
|
||||
found := false
|
||||
for _, c := range current.ProviderSpecific {
|
||||
if d.Name == c.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// provider-specific attribute added
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -19,10 +19,11 @@ package plan
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
)
|
||||
|
||||
type PlanTestSuite struct {
|
||||
@ -38,6 +39,9 @@ type PlanTestSuite struct {
|
||||
bar127AWithProviderSpecificTrue *endpoint.Endpoint
|
||||
bar127AWithProviderSpecificFalse *endpoint.Endpoint
|
||||
bar192A *endpoint.Endpoint
|
||||
multiple1 *endpoint.Endpoint
|
||||
multiple2 *endpoint.Endpoint
|
||||
multiple3 *endpoint.Endpoint
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) SetupTest() {
|
||||
@ -138,7 +142,24 @@ func (suite *PlanTestSuite) SetupTest() {
|
||||
endpoint.ResourceLabelKey: "ingress/default/bar-192",
|
||||
},
|
||||
}
|
||||
|
||||
suite.multiple1 = &endpoint.Endpoint{
|
||||
DNSName: "multiple",
|
||||
Targets: endpoint.Targets{"192.168.0.1"},
|
||||
RecordType: "A",
|
||||
SetIdentifier: "test-set-1",
|
||||
}
|
||||
suite.multiple2 = &endpoint.Endpoint{
|
||||
DNSName: "multiple",
|
||||
Targets: endpoint.Targets{"192.168.0.2"},
|
||||
RecordType: "A",
|
||||
SetIdentifier: "test-set-1",
|
||||
}
|
||||
suite.multiple3 = &endpoint.Endpoint{
|
||||
DNSName: "multiple",
|
||||
Targets: endpoint.Targets{"192.168.0.2"},
|
||||
RecordType: "A",
|
||||
SetIdentifier: "test-set-2",
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestSyncFirstRound() {
|
||||
@ -427,6 +448,50 @@ func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() {
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() {
|
||||
|
||||
current := []*endpoint.Endpoint{suite.multiple1}
|
||||
desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.multiple3}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{suite.multiple1}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.multiple2}
|
||||
expectedDelete := []*endpoint.Endpoint{}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew)
|
||||
validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld)
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestSetIdentifierUpdateCreatesAndDeletes() {
|
||||
|
||||
current := []*endpoint.Endpoint{suite.multiple2}
|
||||
desired := []*endpoint.Endpoint{suite.multiple3}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.multiple3}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{}
|
||||
expectedDelete := []*endpoint.Endpoint{suite.multiple2}
|
||||
|
||||
p := &Plan{
|
||||
Policies: []Policy{&SyncPolicy{}},
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
}
|
||||
|
||||
changes := p.Calculate().Changes
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew)
|
||||
validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld)
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
func TestPlan(t *testing.T) {
|
||||
suite.Run(t, new(PlanTestSuite))
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// TestApply tests that applying a policy results in the correct set of changes.
|
||||
|
@ -28,10 +28,11 @@ import (
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz"
|
||||
"github.com/denverdino/aliyungo/metadata"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
log "github.com/sirupsen/logrus"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,9 +22,9 @@ import (
|
||||
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type MockAlibabaCloudDNSAPI struct {
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -27,21 +28,30 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/route53"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/linki/instrumented_http"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
recordTTL = 300
|
||||
// provider specific key that designates whether an AWS ALIAS record has the EvaluateTargetHealth
|
||||
// field set to true.
|
||||
providerSpecificEvaluateTargetHealth = "aws/evaluate-target-health"
|
||||
providerSpecificEvaluateTargetHealth = "aws/evaluate-target-health"
|
||||
providerSpecificWeight = "aws/weight"
|
||||
providerSpecificRegion = "aws/region"
|
||||
providerSpecificFailover = "aws/failover"
|
||||
providerSpecificGeolocationContinentCode = "aws/geolocation-continent-code"
|
||||
providerSpecificGeolocationCountryCode = "aws/geolocation-country-code"
|
||||
providerSpecificGeolocationSubdivisionCode = "aws/geolocation-subdivision-code"
|
||||
providerSpecificMultiValueAnswer = "aws/multi-value-answer"
|
||||
)
|
||||
|
||||
var (
|
||||
// see: https://docs.aws.amazon.com/general/latest/gr/rande.html#elb_region
|
||||
// and: https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/using-govcloud-endpoints.html
|
||||
canonicalHostedZones = map[string]string{
|
||||
// Application Load Balancers and Classic Load Balancers
|
||||
"us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2",
|
||||
@ -63,6 +73,8 @@ var (
|
||||
"sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU",
|
||||
"cn-north-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y",
|
||||
"cn-northwest-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y",
|
||||
"us-gov-west-1.amazonaws.com": "Z1K6XKP9SAGWDV",
|
||||
"me-south-1.elb.amazonaws.com": "ZS929ML54UICD",
|
||||
// Network Load Balancers
|
||||
"elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP",
|
||||
"elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI",
|
||||
@ -82,6 +94,7 @@ var (
|
||||
"elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU",
|
||||
"elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6",
|
||||
"elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D",
|
||||
"elb.me-south-1.amazonaws.com": "Z3QSRYVP46NYYV",
|
||||
}
|
||||
)
|
||||
|
||||
@ -245,8 +258,10 @@ func (p *AWSProvider) records(zones map[string]*route53.HostedZone) ([]*endpoint
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
f := func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) {
|
||||
for _, r := range resp.ResourceRecordSets {
|
||||
newEndpoints := make([]*endpoint.Endpoint, 0)
|
||||
|
||||
// TODO(linki, ownership): Remove once ownership system is in place.
|
||||
// See: https://github.com/kubernetes-incubator/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
|
||||
// See: https://github.com/kubernetes-sigs/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
|
||||
|
||||
if !supportedRecordType(aws.StringValue(r.Type)) {
|
||||
continue
|
||||
@ -263,7 +278,7 @@ func (p *AWSProvider) records(zones map[string]*route53.HostedZone) ([]*endpoint
|
||||
targets[idx] = aws.StringValue(rr.Value)
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(r.Type), ttl, targets...))
|
||||
newEndpoints = append(newEndpoints, endpoint.NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(r.Type), ttl, targets...))
|
||||
}
|
||||
|
||||
if r.AliasTarget != nil {
|
||||
@ -274,6 +289,36 @@ func (p *AWSProvider) records(zones map[string]*route53.HostedZone) ([]*endpoint
|
||||
ep := endpoint.
|
||||
NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), endpoint.RecordTypeCNAME, ttl, aws.StringValue(r.AliasTarget.DNSName)).
|
||||
WithProviderSpecific(providerSpecificEvaluateTargetHealth, fmt.Sprintf("%t", aws.BoolValue(r.AliasTarget.EvaluateTargetHealth)))
|
||||
newEndpoints = append(newEndpoints, ep)
|
||||
}
|
||||
|
||||
for _, ep := range newEndpoints {
|
||||
if r.SetIdentifier != nil {
|
||||
ep.SetIdentifier = aws.StringValue(r.SetIdentifier)
|
||||
switch {
|
||||
case r.Weight != nil:
|
||||
ep.WithProviderSpecific(providerSpecificWeight, fmt.Sprintf("%d", aws.Int64Value(r.Weight)))
|
||||
case r.Region != nil:
|
||||
ep.WithProviderSpecific(providerSpecificRegion, aws.StringValue(r.Region))
|
||||
case r.Failover != nil:
|
||||
ep.WithProviderSpecific(providerSpecificFailover, aws.StringValue(r.Failover))
|
||||
case r.MultiValueAnswer != nil && aws.BoolValue(r.MultiValueAnswer):
|
||||
ep.WithProviderSpecific(providerSpecificMultiValueAnswer, "")
|
||||
case r.GeoLocation != nil:
|
||||
if r.GeoLocation.ContinentCode != nil {
|
||||
ep.WithProviderSpecific(providerSpecificGeolocationContinentCode, aws.StringValue(r.GeoLocation.ContinentCode))
|
||||
} else {
|
||||
if r.GeoLocation.CountryCode != nil {
|
||||
ep.WithProviderSpecific(providerSpecificGeolocationCountryCode, aws.StringValue(r.GeoLocation.CountryCode))
|
||||
}
|
||||
if r.GeoLocation.SubdivisionCode != nil {
|
||||
ep.WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, aws.StringValue(r.GeoLocation.SubdivisionCode))
|
||||
}
|
||||
}
|
||||
default:
|
||||
// one of the above needs to be set, otherwise SetIdentifier doesn't make sense
|
||||
}
|
||||
}
|
||||
endpoints = append(endpoints, ep)
|
||||
}
|
||||
}
|
||||
@ -479,6 +524,47 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint, recordsCac
|
||||
}
|
||||
}
|
||||
|
||||
setIdentifier := ep.SetIdentifier
|
||||
if setIdentifier != "" {
|
||||
change.ResourceRecordSet.SetIdentifier = aws.String(setIdentifier)
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificWeight); ok {
|
||||
weight, err := strconv.ParseInt(prop.Value, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Failed parsing value of %s: %s: %v; using weight of 0", providerSpecificWeight, prop.Value, err)
|
||||
weight = 0
|
||||
}
|
||||
change.ResourceRecordSet.Weight = aws.Int64(weight)
|
||||
}
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificRegion); ok {
|
||||
change.ResourceRecordSet.Region = aws.String(prop.Value)
|
||||
}
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificFailover); ok {
|
||||
change.ResourceRecordSet.Failover = aws.String(prop.Value)
|
||||
}
|
||||
if _, ok := ep.GetProviderSpecificProperty(providerSpecificMultiValueAnswer); ok {
|
||||
change.ResourceRecordSet.MultiValueAnswer = aws.Bool(true)
|
||||
}
|
||||
|
||||
var geolocation = &route53.GeoLocation{}
|
||||
useGeolocation := false
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGeolocationContinentCode); ok {
|
||||
geolocation.ContinentCode = aws.String(prop.Value)
|
||||
useGeolocation = true
|
||||
} else {
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGeolocationCountryCode); ok {
|
||||
geolocation.CountryCode = aws.String(prop.Value)
|
||||
useGeolocation = true
|
||||
}
|
||||
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGeolocationSubdivisionCode); ok {
|
||||
geolocation.SubdivisionCode = aws.String(prop.Value)
|
||||
useGeolocation = true
|
||||
}
|
||||
}
|
||||
if useGeolocation {
|
||||
change.ResourceRecordSet.GeoLocation = geolocation
|
||||
}
|
||||
}
|
||||
|
||||
return change, dualstack
|
||||
}
|
||||
|
||||
@ -577,7 +663,7 @@ func changesByZone(zones map[string]*route53.HostedZone, changeSet []*route53.Ch
|
||||
|
||||
zones := suitableZones(hostname, zones)
|
||||
if len(zones) == 0 {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.String())
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.String())
|
||||
continue
|
||||
}
|
||||
for _, z := range zones {
|
||||
|
@ -31,11 +31,12 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
sd "github.com/aws/aws-sdk-go/service/servicediscovery"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/linki/instrumented_http"
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,11 +26,12 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
sd "github.com/aws/aws-sdk-go/service/servicediscovery"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// Compile time check for interface conformance
|
||||
|
@ -27,12 +27,13 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/route53"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -184,7 +185,11 @@ func (r *Route53APIStub) ChangeResourceRecordSets(input *route53.ChangeResourceR
|
||||
change.ResourceRecordSet.AliasTarget.DNSName = aws.String(wildcardEscape(ensureTrailingDot(aws.StringValue(change.ResourceRecordSet.AliasTarget.DNSName))))
|
||||
}
|
||||
|
||||
key := aws.StringValue(change.ResourceRecordSet.Name) + "::" + aws.StringValue(change.ResourceRecordSet.Type)
|
||||
setId := ""
|
||||
if change.ResourceRecordSet.SetIdentifier != nil {
|
||||
setId = aws.StringValue(change.ResourceRecordSet.SetIdentifier)
|
||||
}
|
||||
key := aws.StringValue(change.ResourceRecordSet.Name) + "::" + aws.StringValue(change.ResourceRecordSet.Type) + "::" + setId
|
||||
switch aws.StringValue(change.Action) {
|
||||
case route53.ChangeActionCreate:
|
||||
if _, found := recordSets[key]; found {
|
||||
@ -314,6 +319,13 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpoint("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
|
||||
endpoint.NewEndpointWithTTL("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"),
|
||||
endpoint.NewEndpointWithTTL("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "random"),
|
||||
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10"),
|
||||
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20"),
|
||||
endpoint.NewEndpointWithTTL("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificRegion, "us-east-1"),
|
||||
endpoint.NewEndpointWithTTL("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificFailover, "PRIMARY"),
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
})
|
||||
|
||||
records, err := provider.Records()
|
||||
@ -328,6 +340,13 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
|
||||
endpoint.NewEndpointWithTTL("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"),
|
||||
endpoint.NewEndpointWithTTL("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "random"),
|
||||
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10"),
|
||||
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20"),
|
||||
endpoint.NewEndpointWithTTL("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificRegion, "us-east-1"),
|
||||
endpoint.NewEndpointWithTTL("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificFailover, "PRIMARY"),
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -797,7 +816,7 @@ func TestAWSBatchChangeSetExceedingNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
func validateEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) {
|
||||
assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match. %s:%s", endpoints, expected)
|
||||
assert.True(t, testutils.SameEndpoints(endpoints, expected), "actual and expected endpoints don't match. %s:%s", endpoints, expected)
|
||||
}
|
||||
|
||||
func validateAWSZones(t *testing.T, zones map[string]*route53.HostedZone, expected map[string]*route53.HostedZone) {
|
||||
@ -1049,7 +1068,7 @@ func TestAWSSuitableZones(t *testing.T) {
|
||||
{"foobar.example.org.", []*route53.HostedZone{zones["example-org-private"], zones["example-org"]}},
|
||||
|
||||
// all matching private zones are suitable
|
||||
// https://github.com/kubernetes-incubator/external-dns/pull/356
|
||||
// https://github.com/kubernetes-sigs/external-dns/pull/356
|
||||
{"bar.example.org.", []*route53.HostedZone{zones["example-org-private"], zones["bar-example-org-private"], zones["bar-example-org"]}},
|
||||
|
||||
{"foo.bar.example.org.", []*route53.HostedZone{zones["example-org-private"], zones["bar-example-org-private"], zones["bar-example-org"]}},
|
||||
|
@ -26,14 +26,14 @@ import (
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/dns"
|
||||
"github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -49,36 +49,36 @@ type config struct {
|
||||
ClientID string `json:"aadClientId" yaml:"aadClientId"`
|
||||
ClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
|
||||
UseManagedIdentityExtension bool `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"`
|
||||
UserAssignedIdentityID string `json:"userAssignedIdentityID" yaml:"userAssignedIdentityID"`
|
||||
}
|
||||
|
||||
// ZonesClient is an interface of dns.ZoneClient that can be stubbed for testing.
|
||||
type ZonesClient interface {
|
||||
ListByResourceGroup(resourceGroupName string, top *int32) (result dns.ZoneListResult, err error)
|
||||
ListByResourceGroupNextResults(lastResults dns.ZoneListResult) (result dns.ZoneListResult, err error)
|
||||
ListByResourceGroupComplete(ctx context.Context, resourceGroupName string, top *int32) (result dns.ZoneListResultIterator, err error)
|
||||
}
|
||||
|
||||
// RecordsClient is an interface of dns.RecordClient that can be stubbed for testing.
|
||||
type RecordsClient interface {
|
||||
ListByDNSZone(resourceGroupName string, zoneName string, top *int32) (result dns.RecordSetListResult, err error)
|
||||
ListByDNSZoneNextResults(list dns.RecordSetListResult) (result dns.RecordSetListResult, err error)
|
||||
Delete(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, ifMatch string) (result autorest.Response, err error)
|
||||
CreateOrUpdate(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, parameters dns.RecordSet, ifMatch string, ifNoneMatch string) (result dns.RecordSet, err error)
|
||||
// RecordSetsClient is an interface of dns.RecordSetsClient that can be stubbed for testing.
|
||||
type RecordSetsClient interface {
|
||||
ListAllByDNSZoneComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result dns.RecordSetListResultIterator, err error)
|
||||
Delete(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, ifMatch string) (result autorest.Response, err error)
|
||||
CreateOrUpdate(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, parameters dns.RecordSet, ifMatch string, ifNoneMatch string) (result dns.RecordSet, err error)
|
||||
}
|
||||
|
||||
// AzureProvider implements the DNS provider for Microsoft's Azure cloud platform.
|
||||
type AzureProvider struct {
|
||||
domainFilter DomainFilter
|
||||
zoneIDFilter ZoneIDFilter
|
||||
dryRun bool
|
||||
resourceGroup string
|
||||
zonesClient ZonesClient
|
||||
recordsClient RecordsClient
|
||||
domainFilter DomainFilter
|
||||
zoneIDFilter ZoneIDFilter
|
||||
dryRun bool
|
||||
resourceGroup string
|
||||
userAssignedIdentityClientID string
|
||||
zonesClient ZonesClient
|
||||
recordSetsClient RecordSetsClient
|
||||
}
|
||||
|
||||
// NewAzureProvider creates a new Azure provider.
|
||||
//
|
||||
// Returns the provider or an error if a provider could not be created.
|
||||
func NewAzureProvider(configFile string, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, resourceGroup string, dryRun bool) (*AzureProvider, error) {
|
||||
func NewAzureProvider(configFile string, domainFilter DomainFilter, zoneIDFilter 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)
|
||||
@ -93,6 +93,10 @@ func NewAzureProvider(configFile string, domainFilter DomainFilter, zoneIDFilter
|
||||
if resourceGroup != "" {
|
||||
cfg.ResourceGroup = resourceGroup
|
||||
}
|
||||
// If userAssignedIdentityClientID is provided explicitly, override existing one in config file
|
||||
if userAssignedIdentityClientID != "" {
|
||||
cfg.UserAssignedIdentityID = userAssignedIdentityClientID
|
||||
}
|
||||
|
||||
var environment azure.Environment
|
||||
if cfg.Cloud == "" {
|
||||
@ -111,38 +115,26 @@ func NewAzureProvider(configFile string, domainFilter DomainFilter, zoneIDFilter
|
||||
|
||||
zonesClient := dns.NewZonesClientWithBaseURI(environment.ResourceManagerEndpoint, cfg.SubscriptionID)
|
||||
zonesClient.Authorizer = autorest.NewBearerAuthorizer(token)
|
||||
recordsClient := dns.NewRecordSetsClientWithBaseURI(environment.ResourceManagerEndpoint, cfg.SubscriptionID)
|
||||
recordsClient.Authorizer = autorest.NewBearerAuthorizer(token)
|
||||
recordSetsClient := dns.NewRecordSetsClientWithBaseURI(environment.ResourceManagerEndpoint, cfg.SubscriptionID)
|
||||
recordSetsClient.Authorizer = autorest.NewBearerAuthorizer(token)
|
||||
|
||||
provider := &AzureProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: cfg.ResourceGroup,
|
||||
zonesClient: zonesClient,
|
||||
recordsClient: recordsClient,
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: cfg.ResourceGroup,
|
||||
userAssignedIdentityClientID: cfg.UserAssignedIdentityID,
|
||||
zonesClient: zonesClient,
|
||||
recordSetsClient: recordSetsClient,
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// getAccessToken retrieves Azure API access token.
|
||||
func getAccessToken(cfg config, environment azure.Environment) (*adal.ServicePrincipalToken, error) {
|
||||
// Try to retrive token with MSI.
|
||||
if cfg.UseManagedIdentityExtension {
|
||||
log.Info("Using managed identity extension to retrieve access token for Azure API.")
|
||||
msiEndpoint, err := adal.GetMSIVMEndpoint()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the managed service identity endpoint: %v", err)
|
||||
}
|
||||
|
||||
token, err := adal.NewServicePrincipalTokenFromMSI(msiEndpoint, environment.ServiceManagementEndpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the managed service identity token: %v", err)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Try to retrieve token with service principal credentials.
|
||||
// Try to use service principal first, some AKS clusters are in an intermediate state that `UseManagedIdentityExtension` is `true`
|
||||
// and service principal exists. In this case, we still want to use service principal to authenticate.
|
||||
if len(cfg.ClientID) > 0 && len(cfg.ClientSecret) > 0 {
|
||||
log.Info("Using client_id+client_secret to retrieve access token for Azure API.")
|
||||
oauthConfig, err := adal.NewOAuthConfig(environment.ActiveDirectoryEndpoint, cfg.TenantID)
|
||||
@ -157,6 +149,31 @@ func getAccessToken(cfg config, environment azure.Environment) (*adal.ServicePri
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Try to retrive token with MSI.
|
||||
if cfg.UseManagedIdentityExtension {
|
||||
log.Info("Using managed identity extension to retrieve access token for Azure API.")
|
||||
msiEndpoint, err := adal.GetMSIVMEndpoint()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the managed service identity endpoint: %v", err)
|
||||
}
|
||||
|
||||
if cfg.UserAssignedIdentityID != "" {
|
||||
log.Infof("Resolving to user assigned identity, client id is %s.", cfg.UserAssignedIdentityID)
|
||||
token, err := adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, environment.ServiceManagementEndpoint, cfg.UserAssignedIdentityID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the managed service identity token: %v", err)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
log.Info("Resolving to system assigned identity.")
|
||||
token, err := adal.NewServicePrincipalTokenFromMSI(msiEndpoint, environment.ServiceManagementEndpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create the managed service identity token: %v", err)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no credentials provided for Azure API")
|
||||
}
|
||||
|
||||
@ -164,13 +181,14 @@ func getAccessToken(cfg config, environment azure.Environment) (*adal.ServicePri
|
||||
//
|
||||
// Returns the current records or an error if the operation failed.
|
||||
func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
|
||||
zones, err := p.zones()
|
||||
ctx := context.Background()
|
||||
zones, err := p.zones(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, zone := range zones {
|
||||
err := p.iterateRecords(*zone.Name, func(recordSet dns.RecordSet) bool {
|
||||
err := p.iterateRecords(ctx, *zone.Name, func(recordSet dns.RecordSet) bool {
|
||||
if recordSet.Name == nil || recordSet.Type == nil {
|
||||
log.Error("Skipping invalid record set with nil name or type.")
|
||||
return true
|
||||
@ -211,72 +229,63 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
|
||||
//
|
||||
// Returns nil if the operation was successful or an error if the operation failed.
|
||||
func (p *AzureProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
zones, err := p.zones()
|
||||
zones, err := p.zones(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleted, updated := p.mapChanges(zones, changes)
|
||||
p.deleteRecords(deleted)
|
||||
p.updateRecords(updated)
|
||||
p.deleteRecords(ctx, deleted)
|
||||
p.updateRecords(ctx, updated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AzureProvider) zones() ([]dns.Zone, error) {
|
||||
log.Debug("Retrieving Azure DNS zones.")
|
||||
func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) {
|
||||
log.Debugf("Retrieving Azure DNS zones for resource group: %s.", p.resourceGroup)
|
||||
|
||||
var zones []dns.Zone
|
||||
list, err := p.zonesClient.ListByResourceGroup(p.resourceGroup, nil)
|
||||
|
||||
zonesIterator, err := p.zonesClient.ListByResourceGroupComplete(ctx, p.resourceGroup, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for list.Value != nil && len(*list.Value) > 0 {
|
||||
for _, zone := range *list.Value {
|
||||
if zone.Name == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !p.domainFilter.Match(*zone.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !p.zoneIDFilter.Match(*zone.ID) {
|
||||
continue
|
||||
}
|
||||
for zonesIterator.NotDone() {
|
||||
zone := zonesIterator.Value()
|
||||
|
||||
if zone.Name != nil && p.domainFilter.Match(*zone.Name) && p.zoneIDFilter.Match(*zone.ID) {
|
||||
zones = append(zones, zone)
|
||||
}
|
||||
|
||||
list, err = p.zonesClient.ListByResourceGroupNextResults(list)
|
||||
err := zonesIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Found %d Azure DNS zone(s).", len(zones))
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func (p *AzureProvider) iterateRecords(zoneName string, callback func(dns.RecordSet) bool) error {
|
||||
func (p *AzureProvider) iterateRecords(ctx context.Context, zoneName string, callback func(dns.RecordSet) bool) error {
|
||||
log.Debugf("Retrieving Azure DNS records for zone '%s'.", zoneName)
|
||||
|
||||
list, err := p.recordsClient.ListByDNSZone(p.resourceGroup, zoneName, nil)
|
||||
recordSetsIterator, err := p.recordSetsClient.ListAllByDNSZoneComplete(ctx, p.resourceGroup, zoneName, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for list.Value != nil && len(*list.Value) > 0 {
|
||||
for _, recordSet := range *list.Value {
|
||||
if !callback(recordSet) {
|
||||
return nil
|
||||
}
|
||||
for recordSetsIterator.NotDone() {
|
||||
if !callback(recordSetsIterator.Value()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
list, err = p.recordsClient.ListByDNSZoneNextResults(list)
|
||||
err := recordSetsIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -323,7 +332,7 @@ func (p *AzureProvider) mapChanges(zones []dns.Zone, changes *plan.Changes) (azu
|
||||
return deleted, updated
|
||||
}
|
||||
|
||||
func (p *AzureProvider) deleteRecords(deleted azureChangeMap) {
|
||||
func (p *AzureProvider) deleteRecords(ctx context.Context, deleted azureChangeMap) {
|
||||
// Delete records first
|
||||
for zone, endpoints := range deleted {
|
||||
for _, endpoint := range endpoints {
|
||||
@ -332,7 +341,7 @@ func (p *AzureProvider) deleteRecords(deleted azureChangeMap) {
|
||||
log.Infof("Would delete %s record named '%s' for Azure DNS zone '%s'.", endpoint.RecordType, name, zone)
|
||||
} else {
|
||||
log.Infof("Deleting %s record named '%s' for Azure DNS zone '%s'.", endpoint.RecordType, name, zone)
|
||||
if _, err := p.recordsClient.Delete(p.resourceGroup, zone, name, dns.RecordType(endpoint.RecordType), ""); err != nil {
|
||||
if _, err := p.recordSetsClient.Delete(ctx, p.resourceGroup, zone, name, dns.RecordType(endpoint.RecordType), ""); err != nil {
|
||||
log.Errorf(
|
||||
"Failed to delete %s record named '%s' for Azure DNS zone '%s': %v",
|
||||
endpoint.RecordType,
|
||||
@ -346,7 +355,7 @@ func (p *AzureProvider) deleteRecords(deleted azureChangeMap) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AzureProvider) updateRecords(updated azureChangeMap) {
|
||||
func (p *AzureProvider) updateRecords(ctx context.Context, updated azureChangeMap) {
|
||||
for zone, endpoints := range updated {
|
||||
for _, endpoint := range endpoints {
|
||||
name := p.recordSetNameForZone(zone, endpoint)
|
||||
@ -371,7 +380,8 @@ func (p *AzureProvider) updateRecords(updated azureChangeMap) {
|
||||
|
||||
recordSet, err := p.newRecordSet(endpoint)
|
||||
if err == nil {
|
||||
_, err = p.recordsClient.CreateOrUpdate(
|
||||
_, err = p.recordSetsClient.CreateOrUpdate(
|
||||
ctx,
|
||||
p.resourceGroup,
|
||||
zone,
|
||||
name,
|
||||
|
431
provider/azure_private_dns.go
Normal file
431
provider/azure_private_dns.go
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/privatedns/mgmt/privatedns"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type azurePrivateDNSConfig struct {
|
||||
SubscriptionID string `json:"subscriptionId" yaml:"subscriptionId"`
|
||||
ResourceGroup string `json:"resourceGroup" yaml:"resourceGroup"`
|
||||
}
|
||||
|
||||
// PrivateZonesClient is an interface of privatedns.PrivateZoneClient that can be stubbed for testing.
|
||||
type PrivateZonesClient interface {
|
||||
ListByResourceGroupComplete(ctx context.Context, resourceGroupName string, top *int32) (result privatedns.PrivateZoneListResultIterator, err error)
|
||||
}
|
||||
|
||||
// PrivateRecordSetsClient is an interface of privatedns.RecordSetsClient that can be stubbed for testing.
|
||||
type PrivateRecordSetsClient interface {
|
||||
ListComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result privatedns.RecordSetListResultIterator, err error)
|
||||
Delete(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, relativeRecordSetName string, ifMatch string) (result autorest.Response, err error)
|
||||
CreateOrUpdate(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, relativeRecordSetName string, parameters privatedns.RecordSet, ifMatch string, ifNoneMatch string) (result privatedns.RecordSet, err error)
|
||||
}
|
||||
|
||||
// AzurePrivateDNSProvider implements the DNS provider for Microsoft's Azure Private DNS service
|
||||
type AzurePrivateDNSProvider struct {
|
||||
domainFilter DomainFilter
|
||||
zoneIDFilter ZoneIDFilter
|
||||
dryRun bool
|
||||
subscriptionID string
|
||||
resourceGroup string
|
||||
zonesClient PrivateZonesClient
|
||||
recordSetsClient PrivateRecordSetsClient
|
||||
}
|
||||
|
||||
// NewAzurePrivateDNSProvider creates a new Azure Private DNS provider.
|
||||
//
|
||||
// Returns the provider or an error if a provider could not be created.
|
||||
func NewAzurePrivateDNSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, resourceGroup string, subscriptionID string, dryRun bool) (*AzurePrivateDNSProvider, error) {
|
||||
authorizer, err := auth.NewAuthorizerFromEnvironment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zonesClient := privatedns.NewPrivateZonesClient(subscriptionID)
|
||||
zonesClient.Authorizer = authorizer
|
||||
recordSetsClient := privatedns.NewRecordSetsClient(subscriptionID)
|
||||
recordSetsClient.Authorizer = authorizer
|
||||
|
||||
provider := &AzurePrivateDNSProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
subscriptionID: subscriptionID,
|
||||
resourceGroup: resourceGroup,
|
||||
zonesClient: zonesClient,
|
||||
recordSetsClient: recordSetsClient,
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// Records gets the current records.
|
||||
//
|
||||
// Returns the current records or an error if the operation failed.
|
||||
func (p *AzurePrivateDNSProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
|
||||
ctx := context.Background()
|
||||
zones, err := p.zones(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Retrieving Azure Private DNS Records for resource group '%s'", p.resourceGroup)
|
||||
|
||||
for _, zone := range zones {
|
||||
err := p.iterateRecords(ctx, *zone.Name, func(recordSet privatedns.RecordSet) {
|
||||
var recordType string
|
||||
if recordSet.Type == nil {
|
||||
log.Debugf("Skipping invalid record set with missing type.")
|
||||
return
|
||||
}
|
||||
recordType = strings.TrimLeft(*recordSet.Type, "Microsoft.Network/privateDnsZones")
|
||||
|
||||
var name string
|
||||
if recordSet.Name == nil {
|
||||
log.Debugf("Skipping invalid record set with missing name.")
|
||||
return
|
||||
}
|
||||
name = formatAzureDNSName(*recordSet.Name, *zone.Name)
|
||||
|
||||
targets := extractAzurePrivateDNSTargets(&recordSet)
|
||||
if len(targets) == 0 {
|
||||
log.Debugf("Failed to extract targets for '%s' with type '%s'.", name, recordType)
|
||||
return
|
||||
}
|
||||
|
||||
var ttl endpoint.TTL
|
||||
if recordSet.TTL != nil {
|
||||
ttl = endpoint.TTL(*recordSet.TTL)
|
||||
}
|
||||
|
||||
ep := endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...)
|
||||
log.Debugf(
|
||||
"Found %s record for '%s' with target '%s'.",
|
||||
ep.RecordType,
|
||||
ep.DNSName,
|
||||
ep.Targets,
|
||||
)
|
||||
endpoints = append(endpoints, ep)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Returning %d Azure Private DNS Records for resource group '%s'", len(endpoints), p.resourceGroup)
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// ApplyChanges applies the given changes.
|
||||
//
|
||||
// Returns nil if the operation was successful or an error if the operation failed.
|
||||
func (p *AzurePrivateDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
log.Debugf("Received %d changes to process", len(changes.Create)+len(changes.Delete)+len(changes.UpdateNew)+len(changes.UpdateOld))
|
||||
|
||||
zones, err := p.zones(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleted, updated := p.mapChanges(zones, changes)
|
||||
p.deleteRecords(ctx, deleted)
|
||||
p.updateRecords(ctx, updated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) zones(ctx context.Context) ([]privatedns.PrivateZone, error) {
|
||||
log.Debugf("Retrieving Azure Private DNS zones for Resource Group '%s'", p.resourceGroup)
|
||||
|
||||
var zones []privatedns.PrivateZone
|
||||
|
||||
i, err := p.zonesClient.ListByResourceGroupComplete(ctx, p.resourceGroup, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i.NotDone() {
|
||||
zone := i.Value()
|
||||
log.Debugf("Validating Zone: %v", *zone.Name)
|
||||
|
||||
if zone.Name == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !p.domainFilter.Match(*zone.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !p.zoneIDFilter.Match(*zone.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
zones = append(zones, zone)
|
||||
|
||||
err := i.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Found %d Azure Private DNS zone(s).", len(zones))
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) iterateRecords(ctx context.Context, zoneName string, callback func(privatedns.RecordSet)) error {
|
||||
log.Debugf("Retrieving Azure Private DNS Records for zone '%s'.", zoneName)
|
||||
|
||||
i, err := p.recordSetsClient.ListComplete(ctx, p.resourceGroup, zoneName, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i.NotDone() {
|
||||
callback(i.Value())
|
||||
|
||||
err := i.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type azurePrivateDNSChangeMap map[string][]*endpoint.Endpoint
|
||||
|
||||
func (p *AzurePrivateDNSProvider) mapChanges(zones []privatedns.PrivateZone, changes *plan.Changes) (azurePrivateDNSChangeMap, azurePrivateDNSChangeMap) {
|
||||
ignored := map[string]bool{}
|
||||
deleted := azurePrivateDNSChangeMap{}
|
||||
updated := azurePrivateDNSChangeMap{}
|
||||
zoneNameIDMapper := zoneIDName{}
|
||||
for _, z := range zones {
|
||||
if z.Name != nil {
|
||||
zoneNameIDMapper.Add(*z.Name, *z.Name)
|
||||
}
|
||||
}
|
||||
mapChange := func(changeMap azurePrivateDNSChangeMap, change *endpoint.Endpoint) {
|
||||
zone, _ := zoneNameIDMapper.FindZone(change.DNSName)
|
||||
if zone == "" {
|
||||
if _, ok := ignored[change.DNSName]; !ok {
|
||||
ignored[change.DNSName] = true
|
||||
log.Infof("Ignoring changes to '%s' because a suitable Azure Private DNS zone was not found.", change.DNSName)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Ensure the record type is suitable
|
||||
changeMap[zone] = append(changeMap[zone], change)
|
||||
}
|
||||
|
||||
for _, change := range changes.Delete {
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.UpdateOld {
|
||||
mapChange(deleted, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.Create {
|
||||
mapChange(updated, change)
|
||||
}
|
||||
|
||||
for _, change := range changes.UpdateNew {
|
||||
mapChange(updated, change)
|
||||
}
|
||||
return deleted, updated
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) deleteRecords(ctx context.Context, deleted azurePrivateDNSChangeMap) {
|
||||
log.Debugf("Records to be deleted: %d", len(deleted))
|
||||
// Delete records first
|
||||
for zone, endpoints := range deleted {
|
||||
for _, endpoint := range endpoints {
|
||||
name := p.recordSetNameForZone(zone, endpoint)
|
||||
if p.dryRun {
|
||||
log.Infof("Would delete %s record named '%s' for Azure Private DNS zone '%s'.", endpoint.RecordType, name, zone)
|
||||
} else {
|
||||
log.Infof("Deleting %s record named '%s' for Azure Private DNS zone '%s'.", endpoint.RecordType, name, zone)
|
||||
if _, err := p.recordSetsClient.Delete(ctx, p.resourceGroup, zone, privatedns.RecordType(endpoint.RecordType), name, ""); err != nil {
|
||||
log.Errorf(
|
||||
"Failed to delete %s record named '%s' for Azure Private DNS zone '%s': %v",
|
||||
endpoint.RecordType,
|
||||
name,
|
||||
zone,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) updateRecords(ctx context.Context, updated azurePrivateDNSChangeMap) {
|
||||
log.Debugf("Records to be updated: %d", len(updated))
|
||||
for zone, endpoints := range updated {
|
||||
for _, endpoint := range endpoints {
|
||||
name := p.recordSetNameForZone(zone, endpoint)
|
||||
if p.dryRun {
|
||||
log.Infof(
|
||||
"Would update %s record named '%s' to '%s' for Azure Private DNS zone '%s'.",
|
||||
endpoint.RecordType,
|
||||
name,
|
||||
endpoint.Targets,
|
||||
zone,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof(
|
||||
"Updating %s record named '%s' to '%s' for Azure Private DNS zone '%s'.",
|
||||
endpoint.RecordType,
|
||||
name,
|
||||
endpoint.Targets,
|
||||
zone,
|
||||
)
|
||||
|
||||
recordSet, err := p.newRecordSet(endpoint)
|
||||
if err == nil {
|
||||
_, err = p.recordSetsClient.CreateOrUpdate(
|
||||
ctx,
|
||||
p.resourceGroup,
|
||||
zone,
|
||||
privatedns.RecordType(endpoint.RecordType),
|
||||
name,
|
||||
recordSet,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf(
|
||||
"Failed to update %s record named '%s' to '%s' for Azure Private DNS zone '%s': %v",
|
||||
endpoint.RecordType,
|
||||
name,
|
||||
endpoint.Targets,
|
||||
zone,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) recordSetNameForZone(zone string, endpoint *endpoint.Endpoint) string {
|
||||
// Remove the zone from the record set
|
||||
name := endpoint.DNSName
|
||||
name = name[:len(name)-len(zone)]
|
||||
name = strings.TrimSuffix(name, ".")
|
||||
|
||||
// For root, use @
|
||||
if name == "" {
|
||||
return "@"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (p *AzurePrivateDNSProvider) newRecordSet(endpoint *endpoint.Endpoint) (privatedns.RecordSet, error) {
|
||||
var ttl int64 = azureRecordTTL
|
||||
if endpoint.RecordTTL.IsConfigured() {
|
||||
ttl = int64(endpoint.RecordTTL)
|
||||
}
|
||||
switch privatedns.RecordType(endpoint.RecordType) {
|
||||
case privatedns.A:
|
||||
aRecords := make([]privatedns.ARecord, len(endpoint.Targets))
|
||||
for i, target := range endpoint.Targets {
|
||||
aRecords[i] = privatedns.ARecord{
|
||||
Ipv4Address: to.StringPtr(target),
|
||||
}
|
||||
}
|
||||
return privatedns.RecordSet{
|
||||
RecordSetProperties: &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
ARecords: &aRecords,
|
||||
},
|
||||
}, nil
|
||||
case privatedns.CNAME:
|
||||
return privatedns.RecordSet{
|
||||
RecordSetProperties: &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
CnameRecord: &privatedns.CnameRecord{
|
||||
Cname: to.StringPtr(endpoint.Targets[0]),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case privatedns.TXT:
|
||||
return privatedns.RecordSet{
|
||||
RecordSetProperties: &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
TxtRecords: &[]privatedns.TxtRecord{
|
||||
{
|
||||
Value: &[]string{
|
||||
endpoint.Targets[0],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return privatedns.RecordSet{}, fmt.Errorf("unsupported record type '%s'", endpoint.RecordType)
|
||||
}
|
||||
|
||||
// Helper function (shared with test code)
|
||||
func extractAzurePrivateDNSTargets(recordSet *privatedns.RecordSet) []string {
|
||||
properties := recordSet.RecordSetProperties
|
||||
if properties == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Check for A records
|
||||
aRecords := properties.ARecords
|
||||
if aRecords != nil && len(*aRecords) > 0 && (*aRecords)[0].Ipv4Address != nil {
|
||||
targets := make([]string, len(*aRecords))
|
||||
for i, aRecord := range *aRecords {
|
||||
targets[i] = *aRecord.Ipv4Address
|
||||
}
|
||||
return targets
|
||||
}
|
||||
|
||||
// Check for CNAME records
|
||||
cnameRecord := properties.CnameRecord
|
||||
if cnameRecord != nil && cnameRecord.Cname != nil {
|
||||
return []string{*cnameRecord.Cname}
|
||||
}
|
||||
|
||||
// Check for TXT records
|
||||
txtRecords := properties.TxtRecords
|
||||
if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil {
|
||||
values := (*txtRecords)[0].Value
|
||||
if values != nil && len(*values) > 0 {
|
||||
return []string{(*values)[0]}
|
||||
}
|
||||
}
|
||||
return []string{}
|
||||
}
|
433
provider/azure_privatedns_test.go
Normal file
433
provider/azure_privatedns_test.go
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/to"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// mockPrivateZonesClient implements the methods of the Azure Private DNS Zones Client which are used in the Azure Private DNS Provider
|
||||
// and returns static results which are defined per test
|
||||
type mockPrivateZonesClient struct {
|
||||
mockZonesClientIterator *privatedns.PrivateZoneListResultIterator
|
||||
}
|
||||
|
||||
// mockPrivateRecordSetsClient implements the methods of the Azure Private DNS RecordSet Client which are used in the Azure Private DNS Provider
|
||||
// and returns static results which are defined per test
|
||||
type mockPrivateRecordSetsClient struct {
|
||||
mockRecordSetListIterator *privatedns.RecordSetListResultIterator
|
||||
deletedEndpoints []*endpoint.Endpoint
|
||||
updatedEndpoints []*endpoint.Endpoint
|
||||
}
|
||||
|
||||
// mockPrivateZoneListResultPageIterator is used to paginate forward through a list of zones
|
||||
type mockPrivateZoneListResultPageIterator struct {
|
||||
offset int
|
||||
results []privatedns.PrivateZoneListResult
|
||||
}
|
||||
|
||||
// getNextPage provides the next page based on the offset of the mockZoneListResultPageIterator
|
||||
func (m *mockPrivateZoneListResultPageIterator) getNextPage(context.Context, privatedns.PrivateZoneListResult) (privatedns.PrivateZoneListResult, error) {
|
||||
// it assumed that instances of this kind of iterator are only skimmed through once per test
|
||||
// otherwise a real implementation is required, e.g. based on a linked list
|
||||
if m.offset < len(m.results) {
|
||||
m.offset = m.offset + 1
|
||||
return m.results[m.offset-1], nil
|
||||
}
|
||||
|
||||
// paged to last page or empty
|
||||
return privatedns.PrivateZoneListResult{}, nil
|
||||
}
|
||||
|
||||
// mockPrivateRecordSetListResultPageIterator is used to paginate forward through a list of recordsets
|
||||
type mockPrivateRecordSetListResultPageIterator struct {
|
||||
offset int
|
||||
results []privatedns.RecordSetListResult
|
||||
}
|
||||
|
||||
// getNextPage provides the next page based on the offset of the mockRecordSetListResultPageIterator
|
||||
func (m *mockPrivateRecordSetListResultPageIterator) getNextPage(context.Context, privatedns.RecordSetListResult) (privatedns.RecordSetListResult, error) {
|
||||
// it assumed that instances of this kind of iterator are only skimmed through once per test
|
||||
// otherwise a real implementation is required, e.g. based on a linked list
|
||||
if m.offset < len(m.results) {
|
||||
m.offset = m.offset + 1
|
||||
return m.results[m.offset-1], nil
|
||||
}
|
||||
|
||||
// paged to last page or empty
|
||||
return privatedns.RecordSetListResult{}, nil
|
||||
}
|
||||
|
||||
func createMockPrivateZone(zone string, id string) privatedns.PrivateZone {
|
||||
return privatedns.PrivateZone{
|
||||
ID: to.StringPtr(id),
|
||||
Name: to.StringPtr(zone),
|
||||
}
|
||||
}
|
||||
|
||||
func (client *mockPrivateZonesClient) ListByResourceGroupComplete(ctx context.Context, resourceGroupName string, top *int32) (result privatedns.PrivateZoneListResultIterator, err error) {
|
||||
// pre-iterate to first item to emulate behaviour of Azure SDK
|
||||
err = client.mockZonesClientIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return *client.mockZonesClientIterator, err
|
||||
}
|
||||
|
||||
return *client.mockZonesClientIterator, nil
|
||||
}
|
||||
|
||||
func privateARecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
aRecords := make([]privatedns.ARecord, len(values))
|
||||
for i, value := range values {
|
||||
aRecords[i] = privatedns.ARecord{
|
||||
Ipv4Address: to.StringPtr(value),
|
||||
}
|
||||
}
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
ARecords: &aRecords,
|
||||
}
|
||||
}
|
||||
|
||||
func privateCNameRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
CnameRecord: &privatedns.CnameRecord{
|
||||
Cname: to.StringPtr(values[0]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func privateTxtRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
TxtRecords: &[]privatedns.TxtRecord{
|
||||
{
|
||||
Value: &[]string{values[0]},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func privateOthersRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties {
|
||||
return &privatedns.RecordSetProperties{
|
||||
TTL: to.Int64Ptr(ttl),
|
||||
}
|
||||
}
|
||||
func createPrivateMockRecordSet(name, recordType string, values ...string) privatedns.RecordSet {
|
||||
return createPrivateMockRecordSetMultiWithTTL(name, recordType, 0, values...)
|
||||
}
|
||||
func createPrivateMockRecordSetWithTTL(name, recordType, value string, ttl int64) privatedns.RecordSet {
|
||||
return createPrivateMockRecordSetMultiWithTTL(name, recordType, ttl, value)
|
||||
}
|
||||
func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values ...string) privatedns.RecordSet {
|
||||
var getterFunc func(values []string, ttl int64) *privatedns.RecordSetProperties
|
||||
|
||||
switch recordType {
|
||||
case endpoint.RecordTypeA:
|
||||
getterFunc = privateARecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeCNAME:
|
||||
getterFunc = privateCNameRecordSetPropertiesGetter
|
||||
case endpoint.RecordTypeTXT:
|
||||
getterFunc = privateTxtRecordSetPropertiesGetter
|
||||
default:
|
||||
getterFunc = privateOthersRecordSetPropertiesGetter
|
||||
}
|
||||
return privatedns.RecordSet{
|
||||
Name: to.StringPtr(name),
|
||||
Type: to.StringPtr("Microsoft.Network/privateDnsZones" + recordType),
|
||||
RecordSetProperties: getterFunc(values, ttl),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (client *mockPrivateRecordSetsClient) ListComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result privatedns.RecordSetListResultIterator, err error) {
|
||||
// pre-iterate to first item to emulate behaviour of Azure SDK
|
||||
err = client.mockRecordSetListIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return *client.mockRecordSetListIterator, err
|
||||
}
|
||||
|
||||
return *client.mockRecordSetListIterator, nil
|
||||
}
|
||||
|
||||
func (client *mockPrivateRecordSetsClient) Delete(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, relativeRecordSetName string, ifMatch string) (result autorest.Response, err error) {
|
||||
client.deletedEndpoints = append(
|
||||
client.deletedEndpoints,
|
||||
endpoint.NewEndpoint(
|
||||
formatAzureDNSName(relativeRecordSetName, privateZoneName),
|
||||
string(recordType),
|
||||
"",
|
||||
),
|
||||
)
|
||||
return autorest.Response{}, nil
|
||||
}
|
||||
|
||||
func (client *mockPrivateRecordSetsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, relativeRecordSetName string, parameters privatedns.RecordSet, ifMatch string, ifNoneMatch string) (result privatedns.RecordSet, err error) {
|
||||
var ttl endpoint.TTL
|
||||
if parameters.TTL != nil {
|
||||
ttl = endpoint.TTL(*parameters.TTL)
|
||||
}
|
||||
client.updatedEndpoints = append(
|
||||
client.updatedEndpoints,
|
||||
endpoint.NewEndpointWithTTL(
|
||||
formatAzureDNSName(relativeRecordSetName, privateZoneName),
|
||||
string(recordType),
|
||||
ttl,
|
||||
extractAzurePrivateDNSTargets(¶meters)...,
|
||||
),
|
||||
)
|
||||
return parameters, nil
|
||||
}
|
||||
|
||||
// newMockedAzurePrivateDNSProvider creates an AzureProvider comprising the mocked clients for zones and recordsets
|
||||
func newMockedAzurePrivateDNSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, resourceGroup string, zones *[]privatedns.PrivateZone, recordSets *[]privatedns.RecordSet) (*AzurePrivateDNSProvider, error) {
|
||||
// init zone-related parts of the mock-client
|
||||
pageIterator := mockPrivateZoneListResultPageIterator{
|
||||
results: []privatedns.PrivateZoneListResult{
|
||||
{
|
||||
Value: zones,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(pageIterator.getNextPage)
|
||||
mockZoneClientIterator := privatedns.NewPrivateZoneListResultIterator(mockZoneListResultPage)
|
||||
zonesClient := mockPrivateZonesClient{
|
||||
mockZonesClientIterator: &mockZoneClientIterator,
|
||||
}
|
||||
|
||||
// init record-related parts of the mock-client
|
||||
resultPageIterator := mockPrivateRecordSetListResultPageIterator{
|
||||
results: []privatedns.RecordSetListResult{
|
||||
{
|
||||
Value: recordSets,
|
||||
},
|
||||
},
|
||||
}
|
||||
mockRecordSetListResultPage := privatedns.NewRecordSetListResultPage(resultPageIterator.getNextPage)
|
||||
mockRecordSetListIterator := privatedns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
||||
recordSetsClient := mockPrivateRecordSetsClient{
|
||||
mockRecordSetListIterator: &mockRecordSetListIterator,
|
||||
}
|
||||
|
||||
return newAzurePrivateDNSProvider(domainFilter, zoneIDFilter, dryRun, resourceGroup, &zonesClient, &recordSetsClient), nil
|
||||
}
|
||||
|
||||
func newAzurePrivateDNSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, resourceGroup string, privateZonesClient PrivateZonesClient, privateRecordsClient PrivateRecordSetsClient) *AzurePrivateDNSProvider {
|
||||
return &AzurePrivateDNSProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: resourceGroup,
|
||||
zonesClient: privateZonesClient,
|
||||
recordSetsClient: privateRecordsClient,
|
||||
}
|
||||
}
|
||||
|
||||
func TestAzurePrivateDNSRecord(t *testing.T) {
|
||||
provider, err := newMockedAzurePrivateDNSProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s",
|
||||
&[]privatedns.PrivateZone{
|
||||
createMockPrivateZone("example.com", "/privateDnsZones/example.com"),
|
||||
},
|
||||
&[]privatedns.RecordSet{
|
||||
createPrivateMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
|
||||
createPrivateMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
|
||||
createPrivateMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"),
|
||||
createPrivateMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := provider.Records()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122"),
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestAzurePrivateDNSMultiRecord(t *testing.T) {
|
||||
provider, err := newMockedAzurePrivateDNSProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s",
|
||||
&[]privatedns.PrivateZone{
|
||||
createMockPrivateZone("example.com", "/privateDnsZones/example.com"),
|
||||
},
|
||||
&[]privatedns.RecordSet{
|
||||
createPrivateMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
|
||||
createPrivateMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
|
||||
createPrivateMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"),
|
||||
createPrivateMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
|
||||
createPrivateMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := provider.Records()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"),
|
||||
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
|
||||
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
|
||||
}
|
||||
|
||||
validateAzureEndpoints(t, actual, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestAzurePrivateDNSApplyChanges(t *testing.T) {
|
||||
recordsClient := mockPrivateRecordSetsClient{}
|
||||
|
||||
testAzurePrivateDNSApplyChangesInternal(t, false, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
|
||||
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
|
||||
})
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"),
|
||||
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
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("bar.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "other.com"),
|
||||
endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "5.6.7.8"),
|
||||
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAzurePrivateDNSApplyChangesDryRun(t *testing.T) {
|
||||
recordsClient := mockRecordSetsClient{}
|
||||
|
||||
testAzureApplyChangesInternal(t, true, &recordsClient)
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{})
|
||||
|
||||
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{})
|
||||
}
|
||||
|
||||
func testAzurePrivateDNSApplyChangesInternal(t *testing.T, dryRun bool, client PrivateRecordSetsClient) {
|
||||
zlr := privatedns.PrivateZoneListResult{
|
||||
Value: &[]privatedns.PrivateZone{
|
||||
createMockPrivateZone("example.com", "/privateDnsZones/example.com"),
|
||||
createMockPrivateZone("other.com", "/privateDnsZones/other.com"),
|
||||
},
|
||||
}
|
||||
|
||||
results := []privatedns.PrivateZoneListResult{
|
||||
zlr,
|
||||
}
|
||||
|
||||
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(func(ctxParam context.Context, zlrParam privatedns.PrivateZoneListResult) (privatedns.PrivateZoneListResult, error) {
|
||||
if len(results) > 0 {
|
||||
result := results[0]
|
||||
results = nil
|
||||
return result, nil
|
||||
}
|
||||
return privatedns.PrivateZoneListResult{}, nil
|
||||
})
|
||||
mockZoneClientIterator := privatedns.NewPrivateZoneListResultIterator(mockZoneListResultPage)
|
||||
|
||||
zonesClient := mockPrivateZonesClient{
|
||||
mockZonesClientIterator: &mockZoneClientIterator,
|
||||
}
|
||||
|
||||
provider := newAzurePrivateDNSProvider(
|
||||
NewDomainFilter([]string{""}),
|
||||
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.example.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
|
||||
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
|
||||
endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "111.222.111.222"),
|
||||
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
|
||||
endpoint.NewEndpoint("deleted.nope.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)
|
||||
}
|
||||
}
|
@ -20,25 +20,67 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/dns"
|
||||
"github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// mockZonesClient implements the methods of the Azure DNS Zones Client which are used in the Azure Provider
|
||||
// and returns static results which are defined per test
|
||||
type mockZonesClient struct {
|
||||
mockZoneListResult *dns.ZoneListResult
|
||||
mockZonesClientIterator *dns.ZoneListResultIterator
|
||||
}
|
||||
|
||||
type mockRecordsClient struct {
|
||||
mockRecordSet *[]dns.RecordSet
|
||||
deletedEndpoints []*endpoint.Endpoint
|
||||
updatedEndpoints []*endpoint.Endpoint
|
||||
// mockZonesClient implements the methods of the Azure DNS RecordSet Client which are used in the Azure Provider
|
||||
// and returns static results which are defined per test
|
||||
type mockRecordSetsClient struct {
|
||||
mockRecordSetListIterator *dns.RecordSetListResultIterator
|
||||
deletedEndpoints []*endpoint.Endpoint
|
||||
updatedEndpoints []*endpoint.Endpoint
|
||||
}
|
||||
|
||||
// mockZoneListResultPageIterator is used to paginate forward through a list of zones
|
||||
type mockZoneListResultPageIterator struct {
|
||||
offset int
|
||||
results []dns.ZoneListResult
|
||||
}
|
||||
|
||||
// getNextPage provides the next page based on the offset of the mockZoneListResultPageIterator
|
||||
func (m *mockZoneListResultPageIterator) getNextPage(context.Context, dns.ZoneListResult) (dns.ZoneListResult, error) {
|
||||
// it assumed that instances of this kind of iterator are only skimmed through once per test
|
||||
// otherwise a real implementation is required, e.g. based on a linked list
|
||||
if m.offset < len(m.results) {
|
||||
m.offset = m.offset + 1
|
||||
return m.results[m.offset-1], nil
|
||||
}
|
||||
|
||||
// paged to last page or empty
|
||||
return dns.ZoneListResult{}, nil
|
||||
}
|
||||
|
||||
// mockZoneListResultPageIterator is used to paginate forward through a list of recordsets
|
||||
type mockRecordSetListResultPageIterator struct {
|
||||
offset int
|
||||
results []dns.RecordSetListResult
|
||||
}
|
||||
|
||||
// getNextPage provides the next page based on the offset of the mockRecordSetListResultPageIterator
|
||||
func (m *mockRecordSetListResultPageIterator) getNextPage(context.Context, dns.RecordSetListResult) (dns.RecordSetListResult, error) {
|
||||
// it assumed that instances of this kind of iterator are only skimmed through once per test
|
||||
// otherwise a real implementation is required, e.g. based on a linked list
|
||||
if m.offset < len(m.results) {
|
||||
m.offset = m.offset + 1
|
||||
return m.results[m.offset-1], nil
|
||||
}
|
||||
|
||||
// paged to last page or empty
|
||||
return dns.RecordSetListResult{}, nil
|
||||
}
|
||||
|
||||
func createMockZone(zone string, id string) dns.Zone {
|
||||
@ -48,14 +90,14 @@ func createMockZone(zone string, id string) dns.Zone {
|
||||
}
|
||||
}
|
||||
|
||||
func (client *mockZonesClient) ListByResourceGroup(resourceGroupName string, top *int32) (dns.ZoneListResult, error) {
|
||||
// Don't bother filtering by resource group or implementing paging since that's the responsibility
|
||||
// of the Azure DNS service
|
||||
return *client.mockZoneListResult, nil
|
||||
}
|
||||
func (client *mockZonesClient) ListByResourceGroupComplete(ctx context.Context, resourceGroupName string, top *int32) (result dns.ZoneListResultIterator, err error) {
|
||||
// pre-iterate to first item to emulate behaviour of Azure SDK
|
||||
err = client.mockZonesClientIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return *client.mockZonesClientIterator, err
|
||||
}
|
||||
|
||||
func (client *mockZonesClient) ListByResourceGroupNextResults(lastResults dns.ZoneListResult) (dns.ZoneListResult, error) {
|
||||
return dns.ZoneListResult{}, nil
|
||||
return *client.mockZonesClientIterator, nil
|
||||
}
|
||||
|
||||
func aRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties {
|
||||
@ -123,15 +165,17 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values
|
||||
|
||||
}
|
||||
|
||||
func (client *mockRecordsClient) ListByDNSZone(resourceGroupName string, zoneName string, top *int32) (dns.RecordSetListResult, error) {
|
||||
return dns.RecordSetListResult{Value: client.mockRecordSet}, nil
|
||||
func (client *mockRecordSetsClient) ListAllByDNSZoneComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result dns.RecordSetListResultIterator, err error) {
|
||||
// pre-iterate to first item to emulate behaviour of Azure SDK
|
||||
err = client.mockRecordSetListIterator.NextWithContext(ctx)
|
||||
if err != nil {
|
||||
return *client.mockRecordSetListIterator, err
|
||||
}
|
||||
|
||||
return *client.mockRecordSetListIterator, nil
|
||||
}
|
||||
|
||||
func (client *mockRecordsClient) ListByDNSZoneNextResults(list dns.RecordSetListResult) (dns.RecordSetListResult, error) {
|
||||
return dns.RecordSetListResult{}, nil
|
||||
}
|
||||
|
||||
func (client *mockRecordsClient) Delete(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, ifMatch string) (autorest.Response, error) {
|
||||
func (client *mockRecordSetsClient) Delete(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, ifMatch string) (result autorest.Response, err error) {
|
||||
client.deletedEndpoints = append(
|
||||
client.deletedEndpoints,
|
||||
endpoint.NewEndpoint(
|
||||
@ -143,7 +187,7 @@ func (client *mockRecordsClient) Delete(resourceGroupName string, zoneName strin
|
||||
return autorest.Response{}, nil
|
||||
}
|
||||
|
||||
func (client *mockRecordsClient) CreateOrUpdate(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, parameters dns.RecordSet, ifMatch string, ifNoneMatch string) (dns.RecordSet, error) {
|
||||
func (client *mockRecordSetsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, parameters dns.RecordSet, ifMatch string, ifNoneMatch string) (result dns.RecordSet, err error) {
|
||||
var ttl endpoint.TTL
|
||||
if parameters.TTL != nil {
|
||||
ttl = endpoint.TTL(*parameters.TTL)
|
||||
@ -160,14 +204,49 @@ func (client *mockRecordsClient) CreateOrUpdate(resourceGroupName string, zoneNa
|
||||
return parameters, nil
|
||||
}
|
||||
|
||||
func newAzureProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, resourceGroup string, zonesClient ZonesClient, recordsClient RecordsClient) *AzureProvider {
|
||||
// newMockedAzureProvider creates an AzureProvider comprising the mocked clients for zones and recordsets
|
||||
func newMockedAzureProvider(domainFilter DomainFilter, zoneIDFilter 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{
|
||||
{
|
||||
Value: zones,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockZoneListResultPage := dns.NewZoneListResultPage(pageIterator.getNextPage)
|
||||
mockZoneClientIterator := dns.NewZoneListResultIterator(mockZoneListResultPage)
|
||||
zonesClient := mockZonesClient{
|
||||
mockZonesClientIterator: &mockZoneClientIterator,
|
||||
}
|
||||
|
||||
// init record-related parts of the mock-client
|
||||
resultPageIterator := mockRecordSetListResultPageIterator{
|
||||
results: []dns.RecordSetListResult{
|
||||
{
|
||||
Value: recordSets,
|
||||
},
|
||||
},
|
||||
}
|
||||
mockRecordSetListResultPage := dns.NewRecordSetListResultPage(resultPageIterator.getNextPage)
|
||||
mockRecordSetListIterator := dns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
||||
recordSetsClient := mockRecordSetsClient{
|
||||
mockRecordSetListIterator: &mockRecordSetListIterator,
|
||||
}
|
||||
|
||||
return newAzureProvider(domainFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, &zonesClient, &recordSetsClient), nil
|
||||
}
|
||||
|
||||
func newAzureProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider {
|
||||
return &AzureProvider{
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: resourceGroup,
|
||||
zonesClient: zonesClient,
|
||||
recordsClient: recordsClient,
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
dryRun: dryRun,
|
||||
resourceGroup: resourceGroup,
|
||||
userAssignedIdentityClientID: userAssignedIdentityClientID,
|
||||
zonesClient: zonesClient,
|
||||
recordSetsClient: recordsClient,
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,16 +255,11 @@ func validateAzureEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expect
|
||||
}
|
||||
|
||||
func TestAzureRecord(t *testing.T) {
|
||||
zonesClient := mockZonesClient{
|
||||
mockZoneListResult: &dns.ZoneListResult{
|
||||
Value: &[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
provider, err := newMockedAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
}
|
||||
|
||||
recordsClient := mockRecordsClient{
|
||||
mockRecordSet: &[]dns.RecordSet{
|
||||
&[]dns.RecordSet{
|
||||
createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
|
||||
createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
|
||||
createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"),
|
||||
@ -193,10 +267,11 @@ func TestAzureRecord(t *testing.T) {
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
provider := newAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", &zonesClient, &recordsClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := provider.Records()
|
||||
|
||||
@ -216,16 +291,11 @@ func TestAzureRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureMultiRecord(t *testing.T) {
|
||||
zonesClient := mockZonesClient{
|
||||
mockZoneListResult: &dns.ZoneListResult{
|
||||
Value: &[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
provider, err := newMockedAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
},
|
||||
}
|
||||
|
||||
recordsClient := mockRecordsClient{
|
||||
mockRecordSet: &[]dns.RecordSet{
|
||||
&[]dns.RecordSet{
|
||||
createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
|
||||
createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
|
||||
createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"),
|
||||
@ -233,10 +303,11 @@ func TestAzureMultiRecord(t *testing.T) {
|
||||
createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
|
||||
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
|
||||
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
provider := newAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", &zonesClient, &recordsClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := provider.Records()
|
||||
|
||||
@ -256,7 +327,7 @@ func TestAzureMultiRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureApplyChanges(t *testing.T) {
|
||||
recordsClient := mockRecordsClient{}
|
||||
recordsClient := mockRecordSetsClient{}
|
||||
|
||||
testAzureApplyChangesInternal(t, false, &recordsClient)
|
||||
|
||||
@ -282,7 +353,7 @@ func TestAzureApplyChanges(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureApplyChangesDryRun(t *testing.T) {
|
||||
recordsClient := mockRecordsClient{}
|
||||
recordsClient := mockRecordSetsClient{}
|
||||
|
||||
testAzureApplyChangesInternal(t, true, &recordsClient)
|
||||
|
||||
@ -291,20 +362,39 @@ func TestAzureApplyChangesDryRun(t *testing.T) {
|
||||
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{})
|
||||
}
|
||||
|
||||
func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordsClient) {
|
||||
func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsClient) {
|
||||
zlr := dns.ZoneListResult{
|
||||
Value: &[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
createMockZone("other.com", "/dnszones/other.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(
|
||||
NewDomainFilter([]string{""}),
|
||||
NewZoneIDFilter([]string{""}),
|
||||
dryRun,
|
||||
"group",
|
||||
&mockZonesClient{
|
||||
mockZoneListResult: &dns.ZoneListResult{
|
||||
Value: &[]dns.Zone{
|
||||
createMockZone("example.com", "/dnszones/example.com"),
|
||||
createMockZone("other.com", "/dnszones/other.com"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"",
|
||||
&zonesClient,
|
||||
client,
|
||||
)
|
||||
|
||||
|
@ -27,9 +27,9 @@ import (
|
||||
cloudflare "github.com/cloudflare/cloudflare-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/source"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/source"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -284,7 +284,7 @@ func (p *CloudFlareProvider) changesByZone(zones []cloudflare.Zone, changeSet []
|
||||
for _, c := range changeSet {
|
||||
zoneID, _ := zoneNameIDMapper.FindZone(c.ResourceRecordSet[0].Name)
|
||||
if zoneID == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.ResourceRecordSet[0].Name)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSet[0].Name)
|
||||
continue
|
||||
}
|
||||
changes[zoneID] = append(changes[zoneID], c)
|
||||
|
@ -23,10 +23,11 @@ import (
|
||||
"testing"
|
||||
|
||||
cloudflare "github.com/cloudflare/cloudflare-go"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockCloudFlareClient struct{}
|
||||
|
@ -33,8 +33,8 @@ import (
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -21,8 +21,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const defaultCoreDNSPrefix = "/skydns/"
|
||||
|
@ -32,9 +32,9 @@ import (
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/tlsutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/tlsutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -415,7 +415,7 @@ func (p designateProvider) upsertRecordSet(rs *recordSet, managedZones map[strin
|
||||
return err
|
||||
}
|
||||
if rs.zoneID == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", rs.dnsName)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", rs.dnsName)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -447,8 +447,10 @@ func (p designateProvider) upsertRecordSet(rs *recordSet, managedZones map[strin
|
||||
}
|
||||
return p.client.DeleteRecordSet(rs.zoneID, rs.recordSetID)
|
||||
} else {
|
||||
ttl := 0
|
||||
opts := recordsets.UpdateOpts{
|
||||
Records: records,
|
||||
TTL: &ttl,
|
||||
}
|
||||
log.Infof("Updating records: %s/%s: %s", rs.dnsName, rs.recordType, strings.Join(records, ","))
|
||||
if p.dryRun {
|
||||
|
@ -31,8 +31,9 @@ import (
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
|
||||
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
var lastGeneratedDesignateID int32
|
||||
@ -114,7 +115,8 @@ func (c fakeDesignateClient) UpdateRecordSet(zoneID, recordSetID string, opts re
|
||||
if opts.Description != nil {
|
||||
rs.Description = *opts.Description
|
||||
}
|
||||
rs.TTL = opts.TTL
|
||||
rs.TTL = *opts.TTL
|
||||
|
||||
rs.Records = opts.Records
|
||||
return nil
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -323,7 +323,7 @@ func digitalOceanChangesByZone(zones []godo.Domain, changeSet []*DigitalOceanCha
|
||||
for _, c := range changeSet {
|
||||
zone, _ := zoneNameIDMapper.FindZone(c.ResourceRecordSet.Name)
|
||||
if zone == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.ResourceRecordSet.Name)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSet.Name)
|
||||
continue
|
||||
}
|
||||
changes[zone] = append(changes[zone], c)
|
||||
|
@ -23,12 +23,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockDigitalOceanInterface interface {
|
||||
|
@ -24,9 +24,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/dnsimple/dnsimple-go/dnsimple"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const dnsimpleRecordTTL = 3600 // Default TTL of 1 hour if not set (DNSimple's default)
|
||||
@ -235,7 +236,7 @@ func (p *dnsimpleProvider) submitChanges(changes []*dnsimpleChange) error {
|
||||
for _, change := range changes {
|
||||
zone := dnsimpleSuitableZone(change.ResourceRecordSet.Name, zones)
|
||||
if zone == nil {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", change.ResourceRecordSet.Name)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", change.ResourceRecordSet.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,12 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/dnsimple/dnsimple-go/dnsimple"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
var mockProvider dnsimpleProvider
|
||||
|
@ -29,8 +29,8 @@ import (
|
||||
"github.com/nesv/go-dynect/dynect"
|
||||
"github.com/sanyu/dynectsoap/dynectsoap"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"github.com/nesv/go-dynect/dynect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func TestDynMerge_NoUpdateOnTTL0Changes(t *testing.T) {
|
||||
|
@ -21,9 +21,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/exoscale/egoscale"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// EgoscaleClientI for replaceable implementation
|
||||
|
@ -22,9 +22,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/exoscale/egoscale"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type createRecordExoscale struct {
|
||||
|
@ -19,21 +19,21 @@ package provider
|
||||
import (
|
||||
goctx "context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
"github.com/linki/instrumented_http"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
dns "google.golang.org/api/dns/v1"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2/google"
|
||||
|
||||
dns "google.golang.org/api/dns/v1"
|
||||
googleapi "google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -103,6 +103,10 @@ type GoogleProvider struct {
|
||||
project string
|
||||
// Enabled dry-run will print any modifying actions rather than execute them.
|
||||
dryRun bool
|
||||
// Max batch size to submit to Google Cloud DNS per transaction.
|
||||
batchChangeSize int
|
||||
// Interval between batch updates.
|
||||
batchChangeInterval time.Duration
|
||||
// only consider hosted zones managing domains ending in this suffix
|
||||
domainFilter DomainFilter
|
||||
// only consider hosted zones ending with this zone id
|
||||
@ -116,7 +120,7 @@ type GoogleProvider struct {
|
||||
}
|
||||
|
||||
// NewGoogleProvider initializes a new Google CloudDNS based Provider.
|
||||
func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool) (*GoogleProvider, error) {
|
||||
func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, batchChangeSize int, batchChangeInterval time.Duration, dryRun bool) (*GoogleProvider, error) {
|
||||
gcloud, err := google.DefaultClient(context.TODO(), dns.NdevClouddnsReadwriteScope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -129,7 +133,7 @@ func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter Z
|
||||
},
|
||||
})
|
||||
|
||||
dnsClient, err := dns.New(gcloud)
|
||||
dnsClient, err := dns.NewService(context.TODO(), option.WithHTTPClient(gcloud))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -145,6 +149,8 @@ func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter Z
|
||||
provider := &GoogleProvider{
|
||||
project: project,
|
||||
dryRun: dryRun,
|
||||
batchChangeSize: batchChangeSize,
|
||||
batchChangeInterval: batchChangeInterval,
|
||||
domainFilter: domainFilter,
|
||||
zoneIDFilter: zoneIDFilter,
|
||||
resourceRecordSetsClient: resourceRecordSetsService{dnsClient.ResourceRecordSets},
|
||||
@ -289,29 +295,104 @@ func (p *GoogleProvider) submitChange(change *dns.Change) error {
|
||||
// separate into per-zone change sets to be passed to the API.
|
||||
changes := separateChange(zones, change)
|
||||
|
||||
for z, c := range changes {
|
||||
log.Infof("Change zone: %v", z)
|
||||
for _, del := range c.Deletions {
|
||||
log.Infof("Del records: %s %s %s %d", del.Name, del.Type, del.Rrdatas, del.Ttl)
|
||||
}
|
||||
for _, add := range c.Additions {
|
||||
log.Infof("Add records: %s %s %s %d", add.Name, add.Type, add.Rrdatas, add.Ttl)
|
||||
}
|
||||
}
|
||||
for zone, change := range changes {
|
||||
for batch, c := range batchChange(change, p.batchChangeSize) {
|
||||
log.Infof("Change zone: %v batch #%d", zone, batch)
|
||||
for _, del := range c.Deletions {
|
||||
log.Infof("Del records: %s %s %s %d", del.Name, del.Type, del.Rrdatas, del.Ttl)
|
||||
}
|
||||
for _, add := range c.Additions {
|
||||
log.Infof("Add records: %s %s %s %d", add.Name, add.Type, add.Rrdatas, add.Ttl)
|
||||
}
|
||||
|
||||
if p.dryRun {
|
||||
return nil
|
||||
}
|
||||
if p.dryRun {
|
||||
continue
|
||||
}
|
||||
|
||||
for z, c := range changes {
|
||||
if _, err := p.changesClient.Create(p.project, z, c).Do(); err != nil {
|
||||
return err
|
||||
if _, err := p.changesClient.Create(p.project, zone, c).Do(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(p.batchChangeInterval)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// batchChange seperates a zone in multiple transaction.
|
||||
func batchChange(change *dns.Change, batchSize int) []*dns.Change {
|
||||
changes := []*dns.Change{}
|
||||
|
||||
if batchSize == 0 {
|
||||
return append(changes, change)
|
||||
}
|
||||
|
||||
type dnsChange struct {
|
||||
additions []*dns.ResourceRecordSet
|
||||
deletions []*dns.ResourceRecordSet
|
||||
}
|
||||
|
||||
changesByName := map[string]*dnsChange{}
|
||||
|
||||
for _, a := range change.Additions {
|
||||
change, ok := changesByName[a.Name]
|
||||
if !ok {
|
||||
change = &dnsChange{}
|
||||
changesByName[a.Name] = change
|
||||
}
|
||||
|
||||
change.additions = append(change.additions, a)
|
||||
}
|
||||
|
||||
for _, a := range change.Deletions {
|
||||
change, ok := changesByName[a.Name]
|
||||
if !ok {
|
||||
change = &dnsChange{}
|
||||
changesByName[a.Name] = change
|
||||
}
|
||||
|
||||
change.deletions = append(change.deletions, a)
|
||||
}
|
||||
|
||||
names := make([]string, 0)
|
||||
for v := range changesByName {
|
||||
names = append(names, v)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
currentChange := &dns.Change{}
|
||||
var totalChanges int
|
||||
for _, name := range names {
|
||||
c := changesByName[name]
|
||||
|
||||
totalChangesByName := len(c.additions) + len(c.deletions)
|
||||
|
||||
if totalChangesByName > batchSize {
|
||||
log.Warnf("Total changes for %s exceeds max batch size of %d, total changes: %d", name,
|
||||
batchSize, totalChangesByName)
|
||||
continue
|
||||
}
|
||||
|
||||
if totalChanges+totalChangesByName > batchSize {
|
||||
totalChanges = 0
|
||||
changes = append(changes, currentChange)
|
||||
currentChange = &dns.Change{}
|
||||
}
|
||||
|
||||
currentChange.Additions = append(currentChange.Additions, c.additions...)
|
||||
currentChange.Deletions = append(currentChange.Deletions, c.deletions...)
|
||||
|
||||
totalChanges += totalChangesByName
|
||||
}
|
||||
|
||||
if totalChanges > 0 {
|
||||
changes = append(changes, currentChange)
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
// separateChange separates a multi-zone change into a single change per zone.
|
||||
func separateChange(zones map[string]*dns.ManagedZone, change *dns.Change) map[string]*dns.Change {
|
||||
changes := make(map[string]*dns.Change)
|
||||
|
@ -19,25 +19,24 @@ package provider
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
dns "google.golang.org/api/dns/v1"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
dns "google.golang.org/api/dns/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
var (
|
||||
testZones = map[string]*dns.ManagedZone{}
|
||||
testRecords = map[string]map[string]*dns.ResourceRecordSet{}
|
||||
testZones = map[string]*dns.ManagedZone{}
|
||||
testRecords = map[string]map[string]*dns.ResourceRecordSet{}
|
||||
googleDefaultBatchChangeSize = 4000
|
||||
)
|
||||
|
||||
type mockManagedZonesCreateCall struct {
|
||||
@ -551,6 +550,94 @@ func TestSeparateChanges(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGoogleBatchChangeSet(t *testing.T) {
|
||||
cs := &dns.Change{}
|
||||
|
||||
for i := 1; i <= googleDefaultBatchChangeSize; i += 2 {
|
||||
cs.Additions = append(cs.Additions, &dns.ResourceRecordSet{
|
||||
Name: fmt.Sprintf("host-%d.example.org.", i),
|
||||
Ttl: 2,
|
||||
})
|
||||
cs.Deletions = append(cs.Deletions, &dns.ResourceRecordSet{
|
||||
Name: fmt.Sprintf("host-%d.example.org.", i),
|
||||
Ttl: 20,
|
||||
})
|
||||
}
|
||||
|
||||
batchCs := batchChange(cs, googleDefaultBatchChangeSize)
|
||||
|
||||
require.Equal(t, 1, len(batchCs))
|
||||
|
||||
sortChangesByName(cs)
|
||||
validateChange(t, batchCs[0], cs)
|
||||
}
|
||||
|
||||
func TestGoogleBatchChangeSetExceeding(t *testing.T) {
|
||||
cs := &dns.Change{}
|
||||
const testCount = 50
|
||||
const testLimit = 11
|
||||
const expectedBatchCount = 5
|
||||
const expectedChangesCount = 10
|
||||
|
||||
for i := 1; i <= testCount; i += 2 {
|
||||
cs.Additions = append(cs.Additions, &dns.ResourceRecordSet{
|
||||
Name: fmt.Sprintf("host-%d.example.org.", i),
|
||||
Ttl: 2,
|
||||
})
|
||||
cs.Deletions = append(cs.Deletions, &dns.ResourceRecordSet{
|
||||
Name: fmt.Sprintf("host-%d.example.org.", i),
|
||||
Ttl: 20,
|
||||
})
|
||||
}
|
||||
|
||||
batchCs := batchChange(cs, testLimit)
|
||||
|
||||
require.Equal(t, expectedBatchCount, len(batchCs))
|
||||
|
||||
dnsChange := &dns.Change{}
|
||||
for _, c := range batchCs {
|
||||
dnsChange.Additions = append(dnsChange.Additions, c.Additions...)
|
||||
dnsChange.Deletions = append(dnsChange.Deletions, c.Deletions...)
|
||||
}
|
||||
|
||||
require.Equal(t, len(cs.Additions), len(dnsChange.Additions))
|
||||
require.Equal(t, len(cs.Deletions), len(dnsChange.Deletions))
|
||||
|
||||
sortChangesByName(cs)
|
||||
sortChangesByName(dnsChange)
|
||||
|
||||
validateChange(t, dnsChange, cs)
|
||||
}
|
||||
|
||||
func TestGoogleBatchChangeSetExceedingNameChange(t *testing.T) {
|
||||
cs := &dns.Change{}
|
||||
const testCount = 10
|
||||
const testLimit = 1
|
||||
|
||||
cs.Additions = append(cs.Additions, &dns.ResourceRecordSet{
|
||||
Name: "host-1.example.org.",
|
||||
Ttl: 2,
|
||||
})
|
||||
cs.Deletions = append(cs.Deletions, &dns.ResourceRecordSet{
|
||||
Name: "host-1.example.org.",
|
||||
Ttl: 20,
|
||||
})
|
||||
|
||||
batchCs := batchChange(cs, testLimit)
|
||||
|
||||
require.Equal(t, 0, len(batchCs))
|
||||
}
|
||||
|
||||
func sortChangesByName(cs *dns.Change) {
|
||||
sort.SliceStable(cs.Additions, func(i, j int) bool {
|
||||
return cs.Additions[i].Name < cs.Additions[j].Name
|
||||
})
|
||||
|
||||
sort.SliceStable(cs.Deletions, func(i, j int) bool {
|
||||
return cs.Deletions[i].Name < cs.Deletions[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
func validateZones(t *testing.T, zones map[string]*dns.ManagedZone, expected map[string]*dns.ManagedZone) {
|
||||
require.Len(t, zones, len(expected))
|
||||
|
||||
|
@ -25,9 +25,10 @@ import (
|
||||
"strings"
|
||||
|
||||
ibclient "github.com/infobloxopen/infoblox-go-client"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// InfobloxConfig clarifies the method signature
|
||||
|
@ -25,9 +25,10 @@ import (
|
||||
"testing"
|
||||
|
||||
ibclient "github.com/infobloxopen/infoblox-go-client"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockIBConnector struct {
|
||||
|
@ -23,8 +23,8 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -131,7 +131,7 @@ func (im *InMemoryProvider) Records() ([]*endpoint.Endpoint, error) {
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
ep := endpoint.NewEndpoint(record.Name, record.Type, record.Target)
|
||||
ep := endpoint.NewEndpoint(record.Name, record.Type, record.Target).WithSetIdentifier(record.SetIdentifier)
|
||||
ep.Labels = record.Labels
|
||||
endpoints = append(endpoints, ep)
|
||||
}
|
||||
@ -204,10 +204,11 @@ func convertToInMemoryRecord(endpoints []*endpoint.Endpoint) []*inMemoryRecord {
|
||||
records := []*inMemoryRecord{}
|
||||
for _, ep := range endpoints {
|
||||
records = append(records, &inMemoryRecord{
|
||||
Type: ep.RecordType,
|
||||
Name: ep.DNSName,
|
||||
Target: ep.Targets[0],
|
||||
Labels: ep.Labels,
|
||||
Type: ep.RecordType,
|
||||
Name: ep.DNSName,
|
||||
Target: ep.Targets[0],
|
||||
SetIdentifier: ep.SetIdentifier,
|
||||
Labels: ep.Labels,
|
||||
})
|
||||
}
|
||||
return records
|
||||
@ -246,10 +247,11 @@ func (f *filter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[string]st
|
||||
// Name - DNS name assigned to the record
|
||||
// Target - target of the record
|
||||
type inMemoryRecord struct {
|
||||
Type string
|
||||
Name string
|
||||
Target string
|
||||
Labels endpoint.Labels
|
||||
Type string
|
||||
SetIdentifier string
|
||||
Name string
|
||||
Target string
|
||||
Labels endpoint.Labels
|
||||
}
|
||||
|
||||
type zone map[string][]*inMemoryRecord
|
||||
@ -328,15 +330,19 @@ func (c *inMemoryClient) ApplyChanges(ctx context.Context, zoneID string, change
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *inMemoryClient) updateMesh(mesh map[string]map[string]bool, record *inMemoryRecord) error {
|
||||
func (c *inMemoryClient) updateMesh(mesh map[string]map[string]map[string]bool, record *inMemoryRecord) error {
|
||||
if _, exists := mesh[record.Name]; exists {
|
||||
if mesh[record.Name][record.Type] {
|
||||
return ErrDuplicateRecordFound
|
||||
if _, exists := mesh[record.Name][record.Type]; exists {
|
||||
if mesh[record.Name][record.Type][record.SetIdentifier] {
|
||||
return ErrDuplicateRecordFound
|
||||
}
|
||||
mesh[record.Name][record.Type][record.SetIdentifier] = true
|
||||
return nil
|
||||
}
|
||||
mesh[record.Name][record.Type] = true
|
||||
mesh[record.Name][record.Type] = map[string]bool{record.SetIdentifier: true}
|
||||
return nil
|
||||
}
|
||||
mesh[record.Name] = map[string]bool{record.Type: true}
|
||||
mesh[record.Name] = map[string]map[string]bool{record.Type: {record.SetIdentifier: true}}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -346,9 +352,9 @@ func (c *inMemoryClient) validateChangeBatch(zone string, changes *inMemoryChang
|
||||
if !ok {
|
||||
return ErrZoneNotFound
|
||||
}
|
||||
mesh := map[string]map[string]bool{}
|
||||
mesh := map[string]map[string]map[string]bool{}
|
||||
for _, newEndpoint := range changes.Create {
|
||||
if c.findByType(newEndpoint.Type, curZone[newEndpoint.Name]) != nil {
|
||||
if c.findByTypeAndSetIdentifier(newEndpoint.Type, newEndpoint.SetIdentifier, curZone[newEndpoint.Name]) != nil {
|
||||
return ErrRecordAlreadyExists
|
||||
}
|
||||
if err := c.updateMesh(mesh, newEndpoint); err != nil {
|
||||
@ -356,7 +362,7 @@ func (c *inMemoryClient) validateChangeBatch(zone string, changes *inMemoryChang
|
||||
}
|
||||
}
|
||||
for _, updateEndpoint := range changes.UpdateNew {
|
||||
if c.findByType(updateEndpoint.Type, curZone[updateEndpoint.Name]) == nil {
|
||||
if c.findByTypeAndSetIdentifier(updateEndpoint.Type, updateEndpoint.SetIdentifier, curZone[updateEndpoint.Name]) == nil {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
if err := c.updateMesh(mesh, updateEndpoint); err != nil {
|
||||
@ -364,12 +370,12 @@ func (c *inMemoryClient) validateChangeBatch(zone string, changes *inMemoryChang
|
||||
}
|
||||
}
|
||||
for _, updateOldEndpoint := range changes.UpdateOld {
|
||||
if rec := c.findByType(updateOldEndpoint.Type, curZone[updateOldEndpoint.Name]); rec == nil || rec.Target != updateOldEndpoint.Target {
|
||||
if rec := c.findByTypeAndSetIdentifier(updateOldEndpoint.Type, updateOldEndpoint.SetIdentifier, curZone[updateOldEndpoint.Name]); rec == nil || rec.Target != updateOldEndpoint.Target {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
}
|
||||
for _, deleteEndpoint := range changes.Delete {
|
||||
if rec := c.findByType(deleteEndpoint.Type, curZone[deleteEndpoint.Name]); rec == nil || rec.Target != deleteEndpoint.Target {
|
||||
if rec := c.findByTypeAndSetIdentifier(deleteEndpoint.Type, deleteEndpoint.SetIdentifier, curZone[deleteEndpoint.Name]); rec == nil || rec.Target != deleteEndpoint.Target {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
if err := c.updateMesh(mesh, deleteEndpoint); err != nil {
|
||||
@ -379,9 +385,9 @@ func (c *inMemoryClient) validateChangeBatch(zone string, changes *inMemoryChang
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *inMemoryClient) findByType(recordType string, records []*inMemoryRecord) *inMemoryRecord {
|
||||
func (c *inMemoryClient) findByTypeAndSetIdentifier(recordType, setIdentifier string, records []*inMemoryRecord) *inMemoryRecord {
|
||||
for _, record := range records {
|
||||
if record.Type == recordType {
|
||||
if record.Type == recordType && record.SetIdentifier == setIdentifier {
|
||||
return record
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -43,11 +43,12 @@ func TestInMemoryProvider(t *testing.T) {
|
||||
|
||||
func testInMemoryFindByType(t *testing.T) {
|
||||
for _, ti := range []struct {
|
||||
title string
|
||||
findType string
|
||||
records []*inMemoryRecord
|
||||
expected *inMemoryRecord
|
||||
expectedEmpty bool
|
||||
title string
|
||||
findType string
|
||||
findSetIdentifier string
|
||||
records []*inMemoryRecord
|
||||
expected *inMemoryRecord
|
||||
expectedEmpty bool
|
||||
}{
|
||||
{
|
||||
title: "no records, empty type",
|
||||
@ -112,10 +113,32 @@ func testInMemoryFindByType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "multiple records, right type and set identifier",
|
||||
findType: endpoint.RecordTypeA,
|
||||
findSetIdentifier: "test-set-1",
|
||||
records: []*inMemoryRecord{
|
||||
{
|
||||
Type: endpoint.RecordTypeA,
|
||||
SetIdentifier: "test-set-1",
|
||||
},
|
||||
{
|
||||
Type: endpoint.RecordTypeA,
|
||||
SetIdentifier: "test-set-2",
|
||||
},
|
||||
{
|
||||
Type: endpoint.RecordTypeTXT,
|
||||
},
|
||||
},
|
||||
expected: &inMemoryRecord{
|
||||
Type: endpoint.RecordTypeA,
|
||||
SetIdentifier: "test-set-1",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(ti.title, func(t *testing.T) {
|
||||
c := newInMemoryClient()
|
||||
record := c.findByType(ti.findType, ti.records)
|
||||
record := c.findByTypeAndSetIdentifier(ti.findType, ti.findSetIdentifier, ti.records)
|
||||
if ti.expectedEmpty {
|
||||
assert.Nil(t, record)
|
||||
} else {
|
||||
|
@ -22,15 +22,14 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/linode/linodego"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// LinodeDomainClient interface to ease testing
|
||||
@ -504,7 +503,7 @@ func endpointsByZone(zoneNameIDMapper zoneIDName, endpoints []*endpoint.Endpoint
|
||||
for _, ep := range endpoints {
|
||||
zoneID, _ := zoneNameIDMapper.FindZone(ep.DNSName)
|
||||
if zoneID == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", ep.DNSName)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", ep.DNSName)
|
||||
continue
|
||||
}
|
||||
endpointsByZone[zoneID] = append(endpointsByZone[zoneID], ep)
|
||||
|
@ -21,12 +21,13 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/linode/linodego"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type MockDomainClient struct {
|
||||
|
@ -25,12 +25,11 @@ import (
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
api "gopkg.in/ns1/ns1-go.v2/rest"
|
||||
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -309,7 +308,7 @@ func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*
|
||||
for _, c := range changeSets {
|
||||
zone, _ := zoneNameIDMapper.FindZone(c.Endpoint.DNSName)
|
||||
if zone == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Endpoint.DNSName)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.Endpoint.DNSName)
|
||||
continue
|
||||
}
|
||||
changes[zone] = append(changes[zone], c)
|
||||
|
@ -23,13 +23,14 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
api "gopkg.in/ns1/ns1-go.v2/rest"
|
||||
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type MockNS1DomainClient struct {
|
||||
|
@ -27,8 +27,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const ociRecordTTL = 300
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockOCIDNSClient struct{}
|
||||
|
@ -19,23 +19,22 @@ package provider
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
pgo "github.com/ffledgling/pdns-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
pgo "github.com/ffledgling/pdns-go"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/pkg/tlsutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/tlsutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type pdnsChangeType string
|
||||
|
@ -19,17 +19,15 @@ package provider
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
//"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
pgo "github.com/ffledgling/pdns-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
pgo "github.com/ffledgling/pdns-go"
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// FIXME: What do we do about labels?
|
||||
|
@ -21,8 +21,8 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// Provider defines the interface DNS providers should implement.
|
||||
|
@ -23,10 +23,11 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
rc0 "github.com/nic-at/rc0go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// RcodeZeroProvider implements the DNS provider for RcodeZero Anycast DNS.
|
||||
@ -166,7 +167,7 @@ func rcodezeroChangesByZone(zones []*rc0.Zone, changeSet []*rc0.RRSetChange) map
|
||||
for _, c := range changeSet {
|
||||
zone, _ := zoneNameIDMapper.FindZone(c.Name)
|
||||
if zone == "" {
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Name)
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.Name)
|
||||
continue
|
||||
}
|
||||
changes[zone] = append(changes[zone], c)
|
||||
|
@ -22,10 +22,11 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
rc0 "github.com/nic-at/rc0go"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,10 +31,10 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/pkg/errors"
|
||||
"istio.io/istio/pkg/log"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -123,7 +123,7 @@ func (p RDNSProvider) Records() ([]*endpoint.Endpoint, error) {
|
||||
|
||||
for _, r := range rs {
|
||||
domains := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
|
||||
keyToDnsNameSplits(domains)
|
||||
keyToDNSNameSplits(domains)
|
||||
dnsName := strings.Join(domains, ".")
|
||||
if !p.domainFilter.Match(dnsName) {
|
||||
continue
|
||||
@ -250,7 +250,7 @@ func (p *RDNSProvider) filterAndRemoveUseless(ep *endpoint.Endpoint, changes *pl
|
||||
}
|
||||
if !exist {
|
||||
ds := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
|
||||
keyToDnsNameSplits(ds)
|
||||
keyToDNSNameSplits(ds)
|
||||
changes.Delete = append(changes.Delete, &endpoint.Endpoint{
|
||||
DNSName: strings.Join(ds, "."),
|
||||
})
|
||||
@ -455,7 +455,7 @@ func (c etcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRe
|
||||
|
||||
// appendRecords append record to an array
|
||||
func appendRecords(r RDNSRecord, dnsType string, bx map[RDNSRecordType]RDNSRecord, rs []RDNSRecord) ([]RDNSRecord, bool) {
|
||||
dnsName := keyToParentDnsName(r.Key)
|
||||
dnsName := keyToParentDNSName(r.Key)
|
||||
bt := RDNSRecordType{Domain: dnsName, Type: dnsType}
|
||||
if v, ok := bx[bt]; ok {
|
||||
// skip the TXT records if already added to record list.
|
||||
@ -501,12 +501,12 @@ func keyFor(fqdn string) string {
|
||||
return rdnsPrefix + dnsNameToKey(fqdn)
|
||||
}
|
||||
|
||||
// keyToParentDnsName used to get dnsName.
|
||||
// keyToParentDNSName used to get dnsName.
|
||||
// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx => xxx.sample.lb.rancher.cloud
|
||||
// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx/1_1_1_1 => xxx.sample.lb.rancher.cloud
|
||||
func keyToParentDnsName(key string) string {
|
||||
func keyToParentDNSName(key string) string {
|
||||
ds := strings.Split(strings.TrimPrefix(key, rdnsPrefix+"/"), "/")
|
||||
keyToDnsNameSplits(ds)
|
||||
keyToDNSNameSplits(ds)
|
||||
|
||||
dns := strings.Join(ds, ".")
|
||||
prefix := strings.Split(dns, ".")[0]
|
||||
@ -532,9 +532,9 @@ func dnsNameToKey(domain string) string {
|
||||
return "/" + strings.Join(ss, "/")
|
||||
}
|
||||
|
||||
// keyToDnsNameSplits used to reverse etcdv3 path to domain splits.
|
||||
// keyToDNSNameSplits used to reverse etcdv3 path to domain splits.
|
||||
// e.g. /cloud/rancher/lb/sample => [sample lb rancher cloud]
|
||||
func keyToDnsNameSplits(ss []string) {
|
||||
func keyToDNSNameSplits(ss []string) {
|
||||
for i := 0; i < len(ss)/2; i++ {
|
||||
j := len(ss) - i - 1
|
||||
ss[i], ss[j] = ss[j], ss[i]
|
||||
|
@ -27,8 +27,8 @@ import (
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type fakeEtcdv3Client struct {
|
||||
|
@ -28,8 +28,8 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
// rfc2136 provider type
|
||||
|
@ -21,11 +21,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/miekg/dns"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type rfc2136Stub struct {
|
||||
|
@ -6,11 +6,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/transip/gotransip"
|
||||
transip "github.com/transip/gotransip/domain"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -304,7 +305,7 @@ func (p *TransIPProvider) dnsEntriesAreEqual(a, b transip.DNSEntries) bool {
|
||||
continue
|
||||
}
|
||||
|
||||
match += 1
|
||||
match++
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,10 @@ package provider
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/stretchr/testify/assert"
|
||||
transip "github.com/transip/gotransip/domain"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func TestTransIPDnsEntriesAreEqual(t *testing.T) {
|
||||
|
@ -22,10 +22,11 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vinyldns/go-vinyldns/vinyldns"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,11 +22,12 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/vinyldns/go-vinyldns/vinyldns"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockVinyldnsZoneInterface struct {
|
||||
|
@ -20,9 +20,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
)
|
||||
|
||||
// AWSSDRegistry implements registry interface with ownership information associated via the Description field of SD Service
|
||||
|
@ -20,11 +20,12 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type inMemoryProvider struct {
|
||||
|
@ -19,9 +19,9 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
)
|
||||
|
||||
// NoopRegistry implements registry interface without ownership directly propagating changes to dns provider
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user