Skip to content

Commit

Permalink
Add Dyn Provider
Browse files Browse the repository at this point in the history
* add "dyn" provider
* add several --dyn-* args to configure Dyn login
* add github.com/nesv/go-dynect/dynect@0.6.0 to Gopkg and vender/ (the client
  of choice by Terraform)
* make externdns.Version public so it can be stored when committing
  zone changes
* add tutorial for Ingress resources and update root README.md file

Dyn REST API is documented here: https://help.dyn.com/dns-api-knowledge-base/

Example usage:

  external-dns \
    --provider=dyn \
    --dyn-customer-name=acme \
    --dyn-username=acme-api \
    --dyn-password=t0pS3cr3t \
    --domain-filter=portal.acme.com \
    --zone-id-filter=acme.com \
    --namespace=my-test-ns \
    --log-level=debug \
    --txt-prefix=_
  • Loading branch information
jvassev committed Feb 8, 2018
1 parent 35b033a commit 414d394
Show file tree
Hide file tree
Showing 34 changed files with 3,769 additions and 5 deletions.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"]
[[override]]
name = "github.com/kubernetes/repo-infra"
revision = "2d2eb5e12b4663fc4d764b5db9daab39334d3f37"

[[constraint]]
name = "github.com/nesv/go-dynect"
version = "0.6.0"
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ SOURCES = $(shell find . -name '*.go')
IMAGE ?= registry.opensource.zalan.do/teapot/$(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 github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s

build: build/$(BINARY)

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ExternalDNS' current release is `v0.4`. This version allows you to keep selected
* [DigitalOcean](https://www.digitalocean.com/products/networking)
* [DNSimple](https://dnsimple.com/)
* [Infoblox](https://www.infoblox.com/products/dns/)
* [Dyn](https://dyn.com/dns/)

From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.4` with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.

Expand All @@ -47,6 +48,7 @@ The following tutorials are provided:
* [Cloudflare](docs/tutorials/cloudflare.md)
* [DigitalOcean](docs/tutorials/digitalocean.md)
* [Infoblox](docs/tutorials/infoblox.md)
* [Dyn](docs/tutorials/dyn.md)
* Google Container Engine
* [Using Google's Default Ingress Controller](docs/tutorials/gke.md)
* [Using the Nginx Ingress Controller](docs/tutorials/nginx-ingress.md)
Expand Down
145 changes: 145 additions & 0 deletions docs/tutorials/dyn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Setting up ExternalDNS for Dyn

## Creating a Dyn Configuration Secret

For ExternalDNS to access the Dyn API, create a Kubernetes secret.

To create the secret:

```
$ kubectl create secret generic external-dns \
--from-literal=EXTERNAL_DNS_DYN_CUSTOMER_NAME=${DYN_CUSTOMER_NAME} \
--from-literal=EXTERNAL_DNS_DYN_USERNAME=${DYN_USERNAME} \
--from-literal=EXTERNAL_DNS_DYN_PASSWORD=${DYN_PASSWORD}
```

The credentials are the same ones created during account registration. As best practise, you are advised to
create an API-only user that is entitled to only the zones intended to be changed by ExternalDNS

## Deploy ExternalDNS
The rest of this tutorial assumes you own `example.com` domain and your DNS provider is Dyn. Change `example.com`
with a domain/zone that you really own.

In case of the dyn provider, the flag `--zone-id-filter` is mandatory as it specifies which zones to scan for records. Without it


Create a deployment file called `externaldns.yaml` with the following contents:

```
$ cat > externaldns.yaml <<EOF
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:v0.4.8
args:
- --source=ingress
- --txt-prefix=_d
- --namespace=example
- --zone-id-filter=example.com
- --domain-filter=example.com
- --provider=dyn
env:
- name: EXTERNAL_DNS_DYN_CUSTOMER_NAME
valueFrom:
secretKeyRef:
name: external-dns
key: EXTERNAL_DNS_DYN_CUSTOMER_NAME
- name: EXTERNAL_DNS_DYN_USERNAME
valueFrom:
secretKeyRef:
name: external-dns
key: EXTERNAL_DNS_DYN_USERNAME
- name: EXTERNAL_DNS_DYN_PASSWORD
valueFrom:
secretKeyRef:
name: external-dns
key: EXTERNAL_DNS_DYN_PASSWORD
EOF
```

As we'll be creating an Ingress resource, you need `--txt-prefix=_d` as a CNAME cannot coexist with a TXT record. You can change the prefix to
any valid start of a FQDN.

Create the deployment for ExternalDNS:

```
$ kubectl create -f externaldns.yaml
```

## Running a locally build version
If you just want to test ExternalDNS in dry-run mode locally without doing the above deployment you can also do it.
Make sure your kubectl is configured correctly . Assuming you have the sources, build and run it like so:

```bash
make
# output skipped

./build/external-dns \
--provider=dyn \
--dyn-customer-name=${DYN_CUSTOMER_NAME} \
--dyn-username=${DYN_USERNAME} \
--dyn-password=${DYN_PASSWORD} \
--domain-filter=example.com \
--zone-id-filter=example.com \
--namespace=example \
--log-level=debug \
--txt-prefix=_ \
--dry-run=true
INFO[0000] running in dry-run mode. No changes to DNS records will be made.
INFO[0000] Connected to cluster at https://some-k8s-cluster.example.com
INFO[0001] Zones: [example.com]
# output skipped
```

Having `--dry-run=true` and `--log-level=debug` is a great way to see _exactly_ what DynamicDNS is doing or is about to do.

## Deploying an Ingress Resource

Create a file called 'test-ingress.yaml' with the following contents:

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: example
spec:
rules:
- host: test-ingress.example.com
http:
paths:
- backend:
serviceName: my-awesome-service
servicePort: 8080

```

As the DNS name `test-ingress.example.com` matches the filter, external-dns will create two records:
a CNAME for test-ingress.example.com and TXT for _dtest-ingress.example.com.

Create the Igress:

```
$ kubectl create -f test-ingress.yaml
```

By default external-dns scans for changes every minute so give it some time to catch up with the
## Verifying Dyn DNS records

Login to the console at https://portal.dynect.net/login/ and verify records are created

## Clean up

Login to the console at https://portal.dynect.net/login/ and delete the records created. Alternatively, just delete the sample
Ingress resources and external-dns will delete the records.
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ func main() {
DryRun: cfg.DryRun,
},
)
case "dyn":
p, err = provider.NewDynProvider(
provider.DynConfig{
DomainFilter: domainFilter,
ZoneIDFilter: zoneIDFilter,
DryRun: cfg.DryRun,
CustomerName: cfg.DynCustomerName,
Username: cfg.DynUsername,
Password: cfg.DynPassword,
AppVersion: externaldns.Version,
},
)
case "inmemory":
p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
default:
Expand Down
13 changes: 10 additions & 3 deletions pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import (
)

var (
version = "unknown"
// Version is the current version of the app, generated at build time
Version = "unknown"
)

// Config is a project-wide configuration
Expand All @@ -52,6 +53,9 @@ type Config struct {
InfobloxWapiPassword string
InfobloxWapiVersion string
InfobloxSSLVerify bool
DynCustomerName string
DynUsername string
DynPassword string
InMemoryZones []string
Policy string
Registry string
Expand Down Expand Up @@ -117,7 +121,7 @@ func allLogLevelsAsStrings() []string {
// ParseFlags adds and parses flags from command line
func (cfg *Config) ParseFlags(args []string) error {
app := kingpin.New("external-dns", "ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.\n\nNote that all flags may be replaced with env vars - `--flag` -> `EXTERNAL_DNS_FLAG=1` or `--flag value` -> `EXTERNAL_DNS_FLAG=value`")
app.Version(version)
app.Version(Version)
app.DefaultEnvars()

// Flags related to Kubernetes
Expand All @@ -133,7 +137,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal)

// Flags related to providers
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory")
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, dyn, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "dyn", "inmemory")
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("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, specify the Google project (required when --provider=google)").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
Expand All @@ -147,6 +151,9 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("infoblox-wapi-password", "When using the Infoblox provider, specify the WAPI password (required when --provider=infoblox)").Default(defaultConfig.InfobloxWapiPassword).StringVar(&cfg.InfobloxWapiPassword)
app.Flag("infoblox-wapi-version", "When using the Infoblox provider, specify the WAPI version (default: 2.3.1)").Default(defaultConfig.InfobloxWapiVersion).StringVar(&cfg.InfobloxWapiVersion)
app.Flag("infoblox-ssl-verify", "When using the Infoblox provider, specify whether to verify the SSL certificate (default: true, disable with --no-infoblox-ssl-verify)").Default(strconv.FormatBool(defaultConfig.InfobloxSSLVerify)).BoolVar(&cfg.InfobloxSSLVerify)
app.Flag("dyn-customer-name", "When using the Dyn provider, specify the Customer Name").Default("").StringVar(&cfg.DynCustomerName)
app.Flag("dyn-username", "When using the Dyn provider, specify the Username").Default("").StringVar(&cfg.DynUsername)
app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword)
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)

// Flags related to policies
Expand Down
Loading

0 comments on commit 414d394

Please sign in to comment.