Finalized version of ultradns provider

This commit is contained in:
kbhandari 2020-05-11 13:42:47 +05:30 committed by aliasghar
parent 844b787dba
commit db3fdfb1d9
8 changed files with 1832 additions and 32 deletions

4
.github/labeler.yml vendored
View File

@ -65,5 +65,5 @@ provider/vinyldns: provider/vinyldns*
# Add 'provider/vultr' in file which starts with vultr
provider/vultr: provider/vultr*
# Add 'provider/ultradns' in file which starts with vultr
provider/vultr: provider/ultradns*
# Add 'provider/ultradns' in file which starts with ultradns
provider/ultradns: provider/ultradns*

619
docs/tutorials/ultradns.md Normal file
View File

@ -0,0 +1,619 @@
# Setting up ExternalDNS for Services on UltraDNS
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using UltraDNS.
For this tutorial, please make sure that you are using a version **> 0.7.2** of ExternalDNS.
## Managing DNS with UltraDNS
If you would like to read-up on the UltraDNS service, you can find additional details here: [Introduction to UltraDNS](https://docs.ultradns.neustar)
Before proceeding, please create a new DNS Zone that you will create your records in for this tutorial process. For the examples in this tutorial, we will be using `example.com` as our Zone.
## Setting Up UltraDNS Credentials
The following environment variables will be needed to run ExternalDNS with UltraDNS.
`ULTRADNS_USERNAME`,`ULTRADNS_PASSWORD`, &`ULTRADNS_BASEURL`
`ULTRADNS_ACCOUNTNAME`(optional variable).
## Deploying ExternalDNS
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
Then, apply one of the following manifests file to deploy ExternalDNS.
- Note: We are assuming the zone is already present within UltraDNS.
- Note: While creating CNAMES as target endpoints, the `--txt-prefix` option is mandatory.
### Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service
- --source=ingress # ingress is also possible
- --domain-filter=example.com # (Recommended) We recommend to use this filter as it minimize the time to propagate changes, as there are less number of zones to look into..
- --provider=ultradns
- --txt-prefix=txt-
env:
- name: ULTRADNS_USERNAME
value: ""
- name: ULTRADNS_PASSWORD # The password is required to be BASE64 encrypted.
value: ""
- name: ULTRADNS_BASEURL
value: "https://api.ultradns.com/"
- name: ULTRADNS_ACCOUNTNAME
value: ""
```
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
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:latest
args:
- --source=service
- --source=ingress
- --domain-filter=example.com #(Recommended) We recommend to use this filter as it minimize the time to propagate changes, as there are less number of zones to look into..
- --provider=ultradns
- --txt-prefix=txt-
env:
- name: ULTRADNS_USERNAME
value: ""
- name: ULTRADNS_PASSWORD # The password is required to be BASE64 encrypted.
value: ""
- name: ULTRADNS_BASEURL
value: "https://api.ultradns.com/"
- name: ULTRADNS_ACCOUNTNAME
value: ""
```
## Deploying an Nginx Service
Create a service file called 'nginx.yaml' with the following contents:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: my-app.example.com.
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
```
Please note the annotation on the service. Use the same hostname as the UltraDNS zone created above.
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation will cause ExternalDNS to remove the corresponding DNS records.
## Creating the Deployment and Service:
```console
$ kubectl create -f nginx.yaml
$ kubectl create -f external-dns.yaml
```
Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and will synchronize the UltraDNS records.
## Verifying UltraDNS Records
Please verify on the [UltraDNS UI](https://portal.ultradns.neustar) that the records are created under the zone "example.com".
For more information on UltraDNS UI, refer to (https://docs.ultradns.neustar/mspuserguide.html).
Select the zone that was created above (or select the appropriate zone if a different zone was used.)
The external IP address will be displayed as a CNAME record for your zone.
## Cleaning Up the Deployment and Service
Now that we have verified that ExternalDNS will automatically manage your UltraDNS records, you can delete example zones that you created in this tutorial:
```
$ kubectl delete service -f nginx.yaml
$ kubectl delete service -f externaldns.yaml
```
## Creating Multiple A Records Target
- First, you want to create a service file called 'apple-banana-echo.yaml'
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
```
- Then, create service file called 'expose-apple-banana-app.yaml' to expose the services. For more information to deploy ingress controller, refer to (https://kubernetes.github.io/ingress-nginx/deploy/)
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.1,10.10.10.23
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service
servicePort: 5678
```
- Then, create the deployment and service:
```console
$ kubectl create -f apple-banana-echo.yaml
$ kubectl create -f expose-apple-banana-app.yaml
$ kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
- Please verify on the [UltraDNS UI](https://portal.ultradns.neustar) that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone example.com:
```console
$ kubectl delete -f apple-banana-echo.yaml
$ kubectl delete -f expose-apple-banana-app.yaml
$ kubectl delete -f external-dns.yaml
```
## Creating CNAME Record
- Please note, that prior to deploying the external-dns service, you will need to add the option txt-prefix=txt- into external-dns.yaml. If this not provided, your records will not be created.
- First, create a service file called 'apple-banana-echo.yaml'
- _Config File Example kubernetes cluster is on-premise not on cloud_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: apple.cname.com.
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service
servicePort: 5678
```
- _Config File Example Kubernetes cluster service from different cloud vendors_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
annotations:
external-dns.alpha.kubernetes.io/hostname: my-app.example.com.
spec:
selector:
app: apple
type: LoadBalancer
ports:
- protocol: TCP
port: 5678
targetPort: 5678
```
- Then, create the deployment and service:
```console
$ kubectl create -f apple-banana-echo.yaml
$ kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
- Please verify on the [UltraDNS UI](https://portal.ultradns.neustar), that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone "example.com":
```console
$ kubectl delete -f apple-banana-echo.yaml
$ kubectl delete -f external-dns.yaml
```
## Create Multiple Types Of Records
- Please note, that prior to deploying the external-dns service, you will need to add the option txt-prefix=txt- into external-dns.yaml. Since you will also be created a CNAME record, If this not provided, your records will not be created.
- First, create a service file called 'apple-banana-echo.yaml'
- _Config File Example kubernetes cluster is on-premise not on cloud_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app1
labels:
app: apple1
spec:
containers:
- name: apple-app1
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service1
spec:
selector:
app: apple1
ports:
- port: 5679 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app2
labels:
app: apple2
spec:
containers:
- name: apple-app2
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service2
spec:
selector:
app: apple2
ports:
- port: 5680 # Default port for image
apiVersion: extensions/v1beta1
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: apple.cname.com.
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service
servicePort: 5678
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress1
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple-banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service1
servicePort: 5679
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress2
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3,10.10.10.20
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service2
servicePort: 5680
```
- _Config File Example Kubernetes cluster service from different cloud vendors_
```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: my-app.example.com.
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: apple-app1
labels:
app: apple1
spec:
containers:
- name: apple-app1
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service1
spec:
selector:
app: apple1
ports:
- port: 5679 # Default port for image
---
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3,10.10.10.25
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service
servicePort: 5678
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress1
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple-banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3
spec:
rules:
- http:
paths:
- path: /apple
backend:
serviceName: apple-service1
servicePort: 5679
```
- Then, create the deployment and service:
```console
$ kubectl create -f apple-banana-echo.yaml
$ kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
-o Please verify on the [UltraDNS UI](https://portal.ultradns.neustar), that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone "example.com":
```console
$ kubectl delete -f apple-banana-echo.yaml
$ kubectl delete -f external-dns.yaml```

4
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/alecthomas/colour v0.1.0 // indirect
github.com/alecthomas/kingpin v2.2.5+incompatible
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 // indirect
github.com/ultradns/ultradns-sdk-go v1.3.7
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f
github.com/aws/aws-sdk-go v1.27.4
github.com/cloudflare/cloudflare-go v0.10.1
@ -28,6 +29,7 @@ require (
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b // indirect
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
github.com/gophercloud/gophercloud v0.1.0
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/heptio/contour v0.15.0
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65
github.com/linki/instrumented_http v0.2.0
@ -44,7 +46,7 @@ require (
github.com/prometheus/client_golang v1.0.0
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/sirupsen/logrus v1.6.0
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/smartystreets/gunit v1.1.1 // indirect
github.com/stretchr/testify v1.4.0

36
go.sum
View File

@ -71,12 +71,10 @@ github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAK
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/ultradns/ultradns-sdk-go v1.3.7 h1:sGeLtpu5atyi4aXEM18aEz0DpTFyhREhCSfkx4RojfU=
github.com/ultradns/ultradns-sdk-go v1.3.7/go.mod h1:vCC5SBZUcMRpcfma80Aw0Xk11WxOgbDA071AUban7ws=
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=
@ -96,8 +94,6 @@ github.com/aws/aws-sdk-go v1.27.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
@ -105,8 +101,6 @@ github.com/cactus/go-statsd-client v3.1.1+incompatible/go.mod h1:cMRcwZDklk7hXp+
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/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=
@ -194,6 +188,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL
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/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 h1:jmwW6QWvUO2OPe22YfgFvBaaZlSr8Rlrac5lZvG6IdM=
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99/go.mod h1:4mP9w9+vYGw2jUx2+2v03IA+phyQQjNRR4AL3uxlNrs=
@ -206,7 +201,6 @@ 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-kit/kit v0.9.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=
@ -263,8 +257,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
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-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v15.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@ -345,6 +337,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
@ -374,8 +368,6 @@ github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwK
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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=
@ -394,6 +386,8 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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=
@ -511,22 +505,16 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@ -534,8 +522,6 @@ github.com/prometheus/procfs v0.0.0-20190403104016-ea9eea638872/go.mod h1:TjEm7z
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/prom2json v1.1.0/go.mod h1:v7OY1795b9fEUZgq4UU2+15YjRv0LfpxKejIQCy3L7o=
github.com/prometheus/prom2json v1.2.1/go.mod h1:yIcXOj/TLPdtZ12qRyhswPnu+02sfDoqatDjj0WGSvo=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@ -561,6 +547,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
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/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -699,8 +687,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
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/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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=
@ -732,8 +718,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/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=
@ -764,8 +748,6 @@ golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190822000311-fc82fb2afd64/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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=

View File

@ -169,7 +169,7 @@ func main() {
p, err = provider.NewVultrProvider(domainFilter, cfg.DryRun)
case "ultradns":
p, err = provider.NewUltraDNSProvider(domainFilter, cfg.DryRun)
p, err = provider.NewUltraDNSProvider(domainFilter, cfg.DryRun )
case "cloudflare":
p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun)

View File

@ -312,7 +312,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
// Flags related to providers
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "vultr")
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, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "vultr", "ultradns")
app.Flag("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)

511
provider/ultradns.go Normal file
View File

@ -0,0 +1,511 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package provider
import (
"context"
"encoding/base64"
"fmt"
"os"
"strconv"
"strings"
"time"
udnssdk "github.com/ultradns/ultradns-sdk-go"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
const (
ultradnsDefaultTTL = 198
ultradnsCreate = "CREATE"
ultradnsDelete = "DELETE"
ultradnsUpdate = "UPDATE"
sbPoolPriority = 1
sbPoolOrder = "ROUND_ROBIN"
rdPoolOrder = "ROUND_ROBIN"
)
// global variables
var sbPoolRunProbes = true
var sbPoolActOnProbes = true
var ultradnsPoolType = "rdpool"
//Setting custom headers for ultradns api calls
var customHeader = []udnssdk.CustomHeader{
udnssdk.CustomHeader {
Key: "UltraClient",
Value: "kube-client",
},
}
type UltraDNSProvider struct {
client udnssdk.Client
domainFilter endpoint.DomainFilter
DryRun bool
AccountName string
}
type UltraDNSChanges struct {
Action string
ResourceRecordSetUltraDNS udnssdk.RRSet
}
// NewUltraDNSProvider initializes a new UltraDNS DNS based provider
func NewUltraDNSProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*UltraDNSProvider, error) {
Username, ok := os.LookupEnv("ULTRADNS_USERNAME")
udnssdk.SetCustomHeader = customHeader
if !ok {
return nil, fmt.Errorf("no username found")
}
Base64Password, ok := os.LookupEnv("ULTRADNS_PASSWORD")
if !ok {
return nil, fmt.Errorf("no password found")
}
// Base64 Standard Decoding
Password, err := base64.StdEncoding.DecodeString(Base64Password)
if err != nil {
fmt.Printf("Error decoding string: %s ", err.Error())
return nil, err
}
BaseURL, ok := os.LookupEnv("ULTRADNS_BASEURL")
if !ok {
return nil, fmt.Errorf("no baseurl found")
}
AccountName, ok := os.LookupEnv("ULTRADNS_ACCOUNTNAME")
if !ok {
AccountName = ""
}
probeValue, ok := os.LookupEnv("ULTRADNS_ENABLE_PROBING")
if ok {
if (probeValue != "true") && (probeValue != "false") {
return nil, fmt.Errorf("please set proper probe value, the values can be either true or false")
} else {
sbPoolRunProbes, _ = strconv.ParseBool(probeValue)
}
}
actOnProbeValue, ok := os.LookupEnv("ULTRADNS_ENABLE_ACTONPROBE")
if ok {
if (actOnProbeValue != "true") && (actOnProbeValue != "false") {
return nil, fmt.Errorf("please set proper act on probe value, the values can be either true or false")
} else {
sbPoolActOnProbes, _ = strconv.ParseBool(actOnProbeValue)
}
}
poolValue, ok := os.LookupEnv("ULTRADNS_POOL_TYPE")
if ok {
if (poolValue != "sbpool") && (poolValue != "rdpool") {
return nil, fmt.Errorf(" please set proper ULTRADNS_POOL_TYPE, supported types are sbpool or rdpool")
}
ultradnsPoolType = poolValue
}
client, err := udnssdk.NewClient(Username, string(Password), BaseURL)
if err != nil {
return nil, fmt.Errorf("Connection cannot be established")
}
provider := &UltraDNSProvider{
client: *client,
domainFilter: domainFilter,
DryRun: dryRun,
AccountName: AccountName,
}
return provider, nil
}
// Zones returns list of hosted zones
func (p *UltraDNSProvider) Zones(ctx context.Context) ([]udnssdk.Zone, error) {
zoneKey := &udnssdk.ZoneKey{}
if p.AccountName != "" {
zoneKey = &udnssdk.ZoneKey{
Zone: "",
AccountName: p.AccountName,
}
}
zones, err := p.fetchZones(ctx, zoneKey)
if err != nil {
return nil, err
}
return zones, nil
}
func (p *UltraDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
zones, err := p.Zones(ctx)
if err != nil {
return nil, err
}
for _, zone := range zones {
log.Infof("zones : %v", zone)
rrsetType := ""
ownerName := ""
rrsetKey := udnssdk.RRSetKey{
Zone: zone.Properties.Name,
Type: rrsetType,
Name: ownerName,
}
if zone.Properties.ResourceRecordCount != 0 {
records, err := p.fetchRecords(ctx, rrsetKey)
if err != nil {
return nil, err
}
for _, r := range records {
recordTypeArray := strings.Fields(r.RRType)
if supportedRecordType(recordTypeArray[0]) {
log.Infof("owner name %s", r.OwnerName)
name := fmt.Sprintf("%s", r.OwnerName)
// root name is identified by the empty string and should be
// translated to zone name for the endpoint entry.
if r.OwnerName == "" {
name = zone.Properties.Name
}
endPointTTL := endpoint.NewEndpointWithTTL(name, recordTypeArray[0], endpoint.TTL(r.TTL), r.RData...)
endpoints = append(endpoints, endPointTTL)
}
}
}
}
log.Infof("endpoints %v", endpoints)
return endpoints, nil
}
func (p *UltraDNSProvider) fetchRecords(ctx context.Context, k udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
// TODO: Sane Configuration for timeouts / retries
maxerrs := 5
waittime := 5 * time.Second
rrsets := []udnssdk.RRSet{}
errcnt := 0
offset := 0
limit := 1000
for {
reqRrsets, ri, res, err := p.client.RRSets.SelectWithOffsetWithLimit(k, offset, limit)
if err != nil {
if res != nil && res.StatusCode >= 500 {
errcnt = errcnt + 1
if errcnt < maxerrs {
time.Sleep(waittime)
continue
}
}
return rrsets, err
}
for _, rrset := range reqRrsets {
rrsets = append(rrsets, rrset)
}
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
return rrsets, nil
}
offset = ri.ReturnedCount + ri.Offset
continue
}
}
func (p *UltraDNSProvider) fetchZones(ctx context.Context, zoneKey *udnssdk.ZoneKey) ([]udnssdk.Zone, error) {
// Select will list the zone rrsets, paginating through all available results
// TODO: Sane Configuration for timeouts / retries
maxerrs := 5
waittime := 5 * time.Second
zones := []udnssdk.Zone{}
errcnt := 0
offset := 0
limit := 1000
for {
reqZones, ri, res, err := p.client.Zone.SelectWithOffsetWithLimit(zoneKey, offset, limit)
if err != nil {
if res != nil && res.StatusCode >= 500 {
errcnt = errcnt + 1
if errcnt < maxerrs {
time.Sleep(waittime)
continue
}
}
return zones, err
}
for _, zone := range reqZones {
if p.domainFilter.IsConfigured() {
if p.domainFilter.Match(zone.Properties.Name) {
zones = append(zones, zone)
}
} else {
zones = append(zones, zone)
}
}
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
return zones, nil
}
offset = ri.ReturnedCount + ri.Offset
continue
}
}
func (p *UltraDNSProvider) submitChanges(ctx context.Context, changes []*UltraDNSChanges) error {
cnameownerName := "cname"
txtownerName := "txt"
if len(changes) == 0 {
log.Infof("All records are already up to date")
return nil
}
zones, err := p.Zones(ctx)
if err != nil {
return err
}
zoneChanges := seperateChangeByZone(zones, changes)
for zoneName, changes := range zoneChanges {
for _, change := range changes {
if change.ResourceRecordSetUltraDNS.RRType == "CNAME" {
cnameownerName = change.ResourceRecordSetUltraDNS.OwnerName
} else if change.ResourceRecordSetUltraDNS.RRType == "TXT" {
txtownerName = change.ResourceRecordSetUltraDNS.OwnerName
}
if cnameownerName == txtownerName {
rrsetKey := udnssdk.RRSetKey{
Zone: zoneName,
Type: endpoint.RecordTypeCNAME,
Name: change.ResourceRecordSetUltraDNS.OwnerName,
}
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if p.DryRun != true {
_, err = p.client.RRSets.Delete(rrsetKey)
if err != nil {
return err
}
}
return fmt.Errorf("The CNAME and TXT Record name cannot be same please recreate external-dns with - --txt-prefix=")
}
rrsetKey := udnssdk.RRSetKey{
Zone: zoneName,
Type: change.ResourceRecordSetUltraDNS.RRType,
Name: change.ResourceRecordSetUltraDNS.OwnerName,
}
record := udnssdk.RRSet{}
if ((change.ResourceRecordSetUltraDNS.RRType == "A" || change.ResourceRecordSetUltraDNS.RRType == "AAAA" ) && (len(change.ResourceRecordSetUltraDNS.RData) >= 2)) {
if ultradnsPoolType == "sbpool" && change.ResourceRecordSetUltraDNS.RRType == "A" {
sbPoolObject, _ := p.newSBPoolObjectCreation(ctx, change)
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
Profile: sbPoolObject.RawProfile(),
}
} else if ultradnsPoolType == "rdpool" {
rdPoolObject, _ := p.newRDPoolObjectCreation(ctx, change)
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
Profile: rdPoolObject.RawProfile(),
}
}else{
return fmt.Errorf("We do not support Multiple target AAAA records in SB Pool please contact to Neustar for further details")
}
}else {
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
}
}
log.WithFields(log.Fields{
"record": record.OwnerName,
"type": record.RRType,
"ttl": record.TTL,
"action": change.Action,
"zone": zoneName,
"profile": record.Profile,
}).Info("Changing record.")
switch change.Action {
case ultradnsCreate:
if p.DryRun != true {
res, err := p.client.RRSets.Create(rrsetKey, record)
_ = res
if err != nil {
return err
}
}
case ultradnsDelete:
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if p.DryRun != true {
_, err = p.client.RRSets.Delete(rrsetKey)
if err != nil {
return err
}
}
case ultradnsUpdate:
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if p.DryRun != true {
_, err = p.client.RRSets.Update(rrsetKey, record)
if err != nil {
return err
}
}
}
}
}
return nil
}
func (p *UltraDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
combinedChanges := make([]*UltraDNSChanges, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))
log.Infof("value of changes %v,%v,%v", changes.Create, changes.UpdateNew, changes.Delete)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsCreate, changes.Create)...)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsUpdate, changes.UpdateNew)...)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsDelete, changes.Delete)...)
return p.submitChanges(ctx, combinedChanges)
}
func newUltraDNSChanges(action string, endpoints []*endpoint.Endpoint) []*UltraDNSChanges {
changes := make([]*UltraDNSChanges, 0, len(endpoints))
ttl := ultradnsDefaultTTL
for _, e := range endpoints {
if e.RecordTTL.IsConfigured() {
ttl = int(e.RecordTTL)
}
// Adding suffix dot to the record name
recordName := fmt.Sprintf("%s.", e.DNSName)
change := &UltraDNSChanges{
Action: action,
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: e.RecordType,
OwnerName: recordName,
RData: e.Targets,
TTL: ttl,
},
}
changes = append(changes, change)
}
return changes
}
func seperateChangeByZone(zones []udnssdk.Zone, changes []*UltraDNSChanges) map[string][]*UltraDNSChanges {
change := make(map[string][]*UltraDNSChanges)
zoneNameID := zoneIDName{}
for _, z := range zones {
zoneNameID.Add(z.Properties.Name, z.Properties.Name)
change[z.Properties.Name] = []*UltraDNSChanges{}
}
for _, c := range changes {
zone, _ := zoneNameID.FindZone(c.ResourceRecordSetUltraDNS.OwnerName)
if zone == "" {
log.Infof("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSetUltraDNS.OwnerName)
continue
}
change[zone] = append(change[zone], c)
}
return change
}
func (p *UltraDNSProvider) getSpecificRecord(ctx context.Context, rrsetKey udnssdk.RRSetKey) (err error) {
_, err = p.client.RRSets.Select(rrsetKey)
if err != nil {
return fmt.Errorf("no record was found for %v", rrsetKey)
} else {
return nil
}
}
// Creation of SBPoolObject
func (p *UltraDNSProvider) newSBPoolObjectCreation(ctx context.Context, change *UltraDNSChanges) (sbPool udnssdk.SBPoolProfile, err error) {
sbpoolRDataList := []udnssdk.SBRDataInfo{}
for _, _ = range change.ResourceRecordSetUltraDNS.RData {
rrdataInfo := udnssdk.SBRDataInfo{
RunProbes: sbPoolRunProbes,
Priority: sbPoolPriority,
State: "NORMAL",
Threshold: 1,
Weight: nil,
}
sbpoolRDataList = append(sbpoolRDataList, rrdataInfo)
}
sbPoolObject := udnssdk.SBPoolProfile{
Context: udnssdk.SBPoolSchema,
Order: sbPoolOrder,
Description: change.ResourceRecordSetUltraDNS.OwnerName,
MaxActive: len(change.ResourceRecordSetUltraDNS.RData),
MaxServed: len(change.ResourceRecordSetUltraDNS.RData),
RDataInfo: sbpoolRDataList,
RunProbes: sbPoolRunProbes,
ActOnProbes: sbPoolActOnProbes,
}
return sbPoolObject, nil
}
//Creation of RDPoolObject
func (p *UltraDNSProvider) newRDPoolObjectCreation(ctx context.Context, change *UltraDNSChanges) (rdPool udnssdk.RDPoolProfile, err error) {
rdPoolObject := udnssdk.RDPoolProfile{
Context: udnssdk.RDPoolSchema,
Order: rdPoolOrder,
Description: change.ResourceRecordSetUltraDNS.OwnerName,
}
return rdPoolObject, nil
}

686
provider/ultradns_test.go Normal file
View File

@ -0,0 +1,686 @@
/*
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"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"reflect"
_ "strings"
"testing"
udnssdk "github.com/ultradns/ultradns-sdk-go"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
type mockUltraDNSZone struct {
client *udnssdk.Client
}
func (m *mockUltraDNSZone) SelectWithOffsetWithLimit(k *udnssdk.ZoneKey, offset int, limit int) (zones []udnssdk.Zone, ResultInfo udnssdk.ResultInfo, resp *http.Response, err error) {
zones = []udnssdk.Zone{}
zone := udnssdk.Zone{}
zoneJson := `
{
"properties": {
"name":"test-ultradns-provider.com.",
"accountName":"teamrest",
"type":"PRIMARY",
"dnssecStatus":"UNSIGNED",
"status":"ACTIVE",
"owner":"teamrest",
"resourceRecordCount":7,
"lastModifiedDateTime":""
}
}`
if err := json.Unmarshal([]byte(zoneJson), &zone); err != nil {
log.Fatal(err)
}
zones = append(zones, zone)
return zones, udnssdk.ResultInfo{}, nil, nil
}
type mockUltraDNSRecord struct {
client *udnssdk.Client
}
func (m *mockUltraDNSRecord) Create(k udnssdk.RRSetKey, rrset udnssdk.RRSet) (*http.Response, error) {
return nil, nil
}
func (m *mockUltraDNSRecord) Select(k udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
return []udnssdk.RRSet{{
OwnerName: "test-ultradns-provider.com.",
RRType: endpoint.RecordTypeA,
RData: []string{"1.1.1.1"},
TTL: 86400,
}}, nil
}
func (m *mockUltraDNSRecord) SelectWithOffset(k udnssdk.RRSetKey, offset int) ([]udnssdk.RRSet, udnssdk.ResultInfo, *http.Response, error) {
return nil, udnssdk.ResultInfo{}, nil, nil
}
func (m *mockUltraDNSRecord) Update(udnssdk.RRSetKey, udnssdk.RRSet) (*http.Response, error) {
return nil, nil
}
func (m *mockUltraDNSRecord) Delete(k udnssdk.RRSetKey) (*http.Response, error) {
return nil, nil
}
func (m *mockUltraDNSRecord) SelectWithOffsetWithLimit(k udnssdk.RRSetKey, offset int, limit int) (rrsets []udnssdk.RRSet, ResultInfo udnssdk.ResultInfo, resp *http.Response, err error) {
return []udnssdk.RRSet{{
OwnerName: "test-ultradns-provider.com.",
RRType: endpoint.RecordTypeA,
RData: []string{"1.1.1.1"},
TTL: 86400,
}}, udnssdk.ResultInfo{}, nil, nil
}
// NewUltraDNSProvider Test scenario
func TestNewUltraDNSProvider(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Nil(t,err)
_ = os.Unsetenv("ULTRADNS_PASSWORD")
_ = os.Unsetenv("ULTRADNS_USERNAME")
_ = os.Unsetenv("ULTRADNS_BASEURL")
_ = os.Unsetenv("ULTRADNS_ACCOUNTNAME")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Expected to fail %s","formatted")
}
//zones function test scenario
func TestUltraDNSProvider_Zones(t *testing.T) {
mocked := mockUltraDNSZone{}
provider := &UltraDNSProvider{
client: udnssdk.Client{
Zone: &mocked,
},
}
zoneKey := &udnssdk.ZoneKey{
Zone: "",
AccountName: "teamrest",
}
expected, _, _, err := provider.client.Zone.SelectWithOffsetWithLimit(zoneKey, 0, 1000)
assert.Nil(t,err)
zones, err := provider.Zones(context.Background())
assert.Nil(t,err)
assert.Equal(t,reflect.DeepEqual(expected, zones),true)
}
//Records function test case
func TestUltraDNSProvider_Records(t *testing.T) {
mocked := mockUltraDNSRecord{}
mockedDomain := mockUltraDNSZone{}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
rrsetKey := udnssdk.RRSetKey{}
expected, _, _, err := provider.client.RRSets.SelectWithOffsetWithLimit(rrsetKey, 0, 1000)
records, err := provider.Records(context.Background())
assert.Nil(t,err)
for _, v := range records {
assert.Equal(t, fmt.Sprintf("%s.", v.DNSName), expected[0].OwnerName)
assert.Equal(t, v.RecordType, expected[0].RRType)
assert.Equal(t, int(v.RecordTTL), expected[0].TTL)
}
}
//ApplyChanges function testcase
func TestUltraDNSProvider_ApplyChanges(t *testing.T) {
changes := &plan.Changes{}
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes.Create = []*endpoint.Endpoint{
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A"},
{DNSName: "ttl.test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A", RecordTTL: 100},
}
changes.Create = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.2"}, RecordType: "A"}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2", "1.1.2.3", "1.1.2.4"}, RecordType: "A", RecordTTL: 100}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2", "1.1.2.3", "1.1.2.4"}, RecordType: "A", RecordTTL: 100}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A", RecordTTL: 100}}
err := provider.ApplyChanges(context.Background(), changes)
assert.Nilf(t,err,"Should not fail %s","formatted")
}
// Testing function getSpecificRecord
func TestUltraDNSProvider_getSpecificRecord(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
recordSetKey := udnssdk.RRSetKey{
Zone: "test-ultradns-provider.com.",
Type: "A",
Name: "teamrest",
}
err := provider.getSpecificRecord(context.Background(), recordSetKey)
assert.Nil(t,err)
}
//Fail case scenario testing where CNAME and TXT Record name are same
func TestUltraDNSProvider_ApplyChangesCNAME(t *testing.T) {
changes := &plan.Changes{}
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes.Create = []*endpoint.Endpoint{
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "CNAME"},
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "TXT"},
}
err := provider.ApplyChanges(context.Background(), changes)
assert.NotNil(t,err)
}
// This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be avaialble "kubernetes-ultradns-provider-test.com"
func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
providerUltradns, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A"},
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, RecordType: "AAAA", RecordTTL: 100},
}
err = providerUltradns.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
rrsetKey := udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ := providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData[0], "1.1.1.1")
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "ttl.kubernetes-ultradns-provider-test.com.",
Type: "AAAA",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData[0], "2001:db8:85a3:0:0:8a2e:370:7334")
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100},
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100}}
err = providerUltradns.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData[0], "1.1.2.2")
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "ttl.kubernetes-ultradns-provider-test.com.",
Type: "AAAA",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData[0], "2001:db8:85a3:0:0:8a2e:370:7335")
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}}
err = providerUltradns.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ := providerUltradns.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
resp, _ = providerUltradns.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
}
}
// This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be avaialble "kubernetes-ultradns-provider-test.com" for multiple target
func TestUltraDNSProvider_ApplyChanges_MultipleTarget_integeration(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.1.2.2"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
rrsetKey := udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ := provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData, []string{"1.1.1.1", "1.1.2.2"})
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24", "1.2.3.4"}, RecordType: "A", RecordTTL: 100}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData, []string{"1.1.2.2", "192.168.0.24", "1.2.3.4"})
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, rrsets[0].RData, []string{"1.1.2.2"})
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
}
}
// Test case to check sbpool creation
func TestUltraDNSProvider_newSBPoolObjectCreation(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
sbpoolRDataList := []udnssdk.SBRDataInfo{}
changes := &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com.", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A", RecordTTL: 100}}
changesList := &UltraDNSChanges{
Action: "UPDATE",
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: "A",
OwnerName: "kubernetes-ultradns-provider-test.com.",
RData: []string{"1.1.2.2", "192.168.0.24"},
TTL: 100,
},
}
for _, _ = range changesList.ResourceRecordSetUltraDNS.RData {
rrdataInfo := udnssdk.SBRDataInfo{
RunProbes: true,
Priority: 1,
State: "NORMAL",
Threshold: 1,
Weight: nil,
}
sbpoolRDataList = append(sbpoolRDataList, rrdataInfo)
}
sbPoolObject := udnssdk.SBPoolProfile{
Context: udnssdk.SBPoolSchema,
Order: "ROUND_ROBIN",
Description: "kubernetes-ultradns-provider-test.com.",
MaxActive: 2,
MaxServed: 2,
RDataInfo: sbpoolRDataList,
RunProbes: true,
ActOnProbes: true,
}
actualSBPoolObject, _ := provider.newSBPoolObjectCreation(context.Background(), changesList)
assert.Equal(t, sbPoolObject, actualSBPoolObject)
}
//Testcase to check fail scenario for multiple AAAA targets
func TestUltraDNSProvider_MultipleTargetAAAA(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
_ = os.Setenv("ULTRADNS_POOL_TYPE","sbpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
}
err := provider.ApplyChanges(context.Background(), changes)
assert.NotNilf(t,err,"We wanted it to fail since multiple AAAA targets are not allowed %s","formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
_ = os.Unsetenv("ULTRADNS_POOL_TYPE")
}
}
//Testcase to check fail scenario for multiple AAAA targets
func TestUltraDNSProvider_MultipleTargetAAAARDPool(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
_ = os.Setenv("ULTRADNS_POOL_TYPE","rdpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
}
err := provider.ApplyChanges(context.Background(), changes)
assert.Nilf(t,err," multiple AAAA targets are allowed when pool is RDPool %s","formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "200 OK")
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
}
}
// Test case to check multiple CNAME targets.
func TestUltraDNSProvider_MultipleTargetCNAME(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"nginx.loadbalancer.com.", "nginx1.loadbalancer.com."}, RecordType: "CNAME", RecordTTL: 100},
}
err = provider.ApplyChanges(context.Background(), changes)
assert.NotNilf(t,err,"We wanted it to fail since multiple CNAME targets are not allowed %s","formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/CNAME/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
}
}
//Testing creation of RD Pool
func TestUltraDNSProvider_newRDPoolObjectCreation(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes := &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com.", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A", RecordTTL: 100}}
changesList := &UltraDNSChanges{
Action: "UPDATE",
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: "A",
OwnerName: "kubernetes-ultradns-provider-test.com.",
RData: []string{"1.1.2.2", "192.168.0.24"},
TTL: 100,
},
}
rdPoolObject := udnssdk.RDPoolProfile{
Context: udnssdk.RDPoolSchema,
Order: "ROUND_ROBIN",
Description: "kubernetes-ultradns-provider-test.com.",
}
actualRDPoolObject, _ := provider.newRDPoolObjectCreation(context.Background(), changesList)
assert.Equal(t, rdPoolObject, actualRDPoolObject)
}
//Testing Failure scenarios over NewUltraDNS Provider
func TestNewUltraDNSProvider_FailCases(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_POOL_TYPE", "xyz")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Pool Type other than given type not working %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_PROBING", "adefg")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Probe value other than given values not working %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "adefg")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"ActOnProbe value other than given values not working %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Unsetenv("ULTRADNS_PASSWORD")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Expected to give error if password is not set %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Unsetenv("ULTRADNS_BASEURL")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Expected to give error if baseurl is not set %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Unsetenv("ULTRADNS_ACCOUNTNAME")
_ = os.Unsetenv("ULTRADNS_ENABLE_ACTONPROBE")
_ = os.Unsetenv("ULTRADNS_ENABLE_PROBING")
_ = os.Unsetenv("ULTRADNS_POOL_TYPE")
_, accounterr := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Nil(t,accounterr)
}
// Testing success scenarios for newly introduced environment variables
func TestNewUltraDNSProvider_NewEnvVariableSuccessCases(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Nilf(t,err,"Pool Type not working in proper scenario %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_PROBING", "false")
_, err1 := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Nilf(t,err1,"Probe given value is not working %s","formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true")
_, err2 := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Nilf(t,err2,"ActOnProbe given value is not working %s","formatted")
}
// Base64 Bad string decoding scenario
func TestNewUltraDNSProvider_Base64DecodeFailcase(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "12345")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NotNilf(t,err,"Base64 decode should fail in this case %s","formatted")
}
func TestUltraDNSProvider_PoolConversionCase(t *testing.T){
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
//Creating SBPool Record
_ = os.Setenv("ULTRADNS_POOL_TYPE","sbpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.4"}, RecordType: "A", RecordTTL: 100}}
err := provider.ApplyChanges(context.Background(), changes)
assert.Nilf(t,err," multiple A record creation with SBPool %s","formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "200 OK")
//Coverting to RD Pool
_ = os.Setenv("ULTRADNS_POOL_TYPE","rdpool")
provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1","1.2.3.5"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "200 OK")
//Coverting back to SB Pool
_ = os.Setenv("ULTRADNS_POOL_TYPE","sbpool")
provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1","1.2.3.4"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "200 OK")
//Deleting Record
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1","1.2.3.4"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.Nil(t,err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, resp.Status, "404 Not Found")
}
}