Add Scaleway DNS as a new provider

Signed-off-by: Patrik Cyvoct <patrik@ptrk.io>
This commit is contained in:
Patrik Cyvoct 2020-06-23 19:38:11 +02:00
parent 8bef3758c7
commit 9a7a873ce8
No known key found for this signature in database
GPG Key ID: 4334D82B950FB63A
9 changed files with 1236 additions and 72 deletions

View File

@ -46,6 +46,7 @@ ExternalDNS' current release is `v0.7`. This version allows you to keep selected
* [TransIP](https://www.transip.eu/domain-name/)
* [VinylDNS](https://www.vinyldns.io)
* [OVH](https://www.ovh.com)
* [Scaleway](https://www.scaleway.com)
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
@ -97,6 +98,7 @@ The following table clarifies the current status of the providers according to t
| RancherDNS | Alpha | |
| Akamai FastDNS | Alpha | |
| OVH | Alpha | |
| Scaleway DNS | Alpha | @Sh4d1 |
| Vultr | Alpha | |
| UltraDNS | Alpha | |
@ -147,6 +149,7 @@ The following tutorials are provided:
* [TransIP](docs/tutorials/transip.md)
* [VinylDNS](docs/tutorials/vinyldns.md)
* [OVH](docs/tutorials/ovh.md)
* [Scaleway](docs/tutorials/scaleway.md)
* [Vultr](docs/tutorials/vultr.md)
* [UltraDNS](docs/tutorials/ultradns.md)

209
docs/tutorials/scaleway.md Normal file
View File

@ -0,0 +1,209 @@
# Setting up ExternalDNS for Services on Scaleway
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Scaleway DNS.
Make sure to use **>=0.7.3** version of ExternalDNS for this tutorial.
**Warning**: Scaleway DNS is currently in Public Beta and may not be suited for production usage.
## Importing a Domain into Scaleway DNS
In order to use your domain, you need to import it into Scaleway DNS. If it's not already done, you can follow [this documentation](https://www.scaleway.com/en/docs/scaleway-dns/)
Once the domain is imported you can either use the root zone, or create a subzone to use.
In this example we will use `example.com` as an example.
## Creating Scaleway Credentials
To use ExternalDNS with Scaleway DNS, you need to create an API token (composed of the Access Key and the Secret Key).
You can either use existing ones or you can create a new token, as explained in [How to generate an API token](https://www.scaleway.com/en/docs/generate-an-api-token/) or directly by going to the [credentials page](https://console.scaleway.com/account/organization/credentials).
Note that you will also need to the Organization ID, which can be retrieve on the same page.
Three environment variables are needed to run ExternalDNS with Scaleway DNS:
- `SCW_ACCESS_KEY` which is the Access Key.
- `SCW_SECRET_KEY` which is the Secret Key.
- `SCW_DEFAULT_ORGANIZATION_ID` which is your Organization ID.
## Deploy ExternalDNS
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
Then apply one of the following manifests file to deploy ExternalDNS.
The following example are suited for development. For a production usage, prefer secrets over environment, and use a [tagged release](https://github.com/kubernetes-sigs/external-dns/releases).
### Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
replicas: 1
selector:
matchLabels:
app: external-dns
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: 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=scaleway
env:
- name: SCW_ACCESS_KEY
value: "<your access key>"
- name: SCW_SECRET_KEY
value: "<your secret key>"
- name: SCW_DEFAULT_ORGANIZATION_ID
value: "<your organization ID>"
```
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
replicas: 1
selector:
matchLabels:
app: external-dns
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: 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=scaleway
env:
- name: SCW_ACCESS_KEY
value: "<your access key>"
- name: SCW_SECRET_KEY
value: "<your secret key>"
- name: SCW_DEFAULT_ORGANIZATION_ID
value: "<your organization ID>"
```
## Deploying an Nginx Service
Create a service file called 'nginx.yaml' with the following contents:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: my-app.example.com
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
```
Note the annotation on the service; use the same hostname as the Scaleway DNS zone created above.
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation will cause ExternalDNS to remove the corresponding DNS records.
Create the deployment and service:
```console
$ kubectl create -f nginx.yaml
```
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize the Scaleway DNS records.
## Verifying Scaleway DNS records
Check your [Scaleway DNS UI](https://console.scaleway.com/domains/external) to view the records for your Scaleway DNS zone.
Click on the zone for the one created above if a different domain was used.
This should show the external IP address of the service as the A record for your domain.
## Cleanup
Now that we have verified that ExternalDNS will automatically manage Scaleway DNS records, we can delete the tutorial's example:
```
$ kubectl delete service -f nginx.yaml
$ kubectl delete service -f externaldns.yaml
```

1
go.mod
View File

@ -46,6 +46,7 @@ require (
github.com/projectcontour/contour v1.5.0
github.com/prometheus/client_golang v1.7.1
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301
github.com/sirupsen/logrus v1.6.0
github.com/smartystreets/gunit v1.3.4 // indirect
github.com/stretchr/testify v1.5.1

73
go.sum
View File

@ -25,7 +25,6 @@ github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKn
github.com/Azure/go-autorest/autorest v0.11.4 h1:iWJqGEvip7mjibEqC/srXNdo+4wLEPiwlP/7dZLtoPc=
github.com/Azure/go-autorest/autorest v0.11.4/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.9.2 h1:Aze/GQeAN1RRbGmnUJvUj+tFGBzFdIg3293/A9rbxC4=
github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
@ -38,17 +37,14 @@ github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8K
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c=
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
@ -63,8 +59,6 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5 h1:OU+AFpBEhyclrQGx4I6zpCx5WvXiKqvFeeOASOmhKCY=
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11 h1:QGjNHMwoPYxE5NpOAc8kpd2KTY293/oFk5BWdjkza+k=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11/go.mod h1:L+HB2uBoDgi3+r1pJEJcbGwyyHhd2QXaGsKLbDwtm8Q=
@ -85,7 +79,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357 h1:3ynCSeUh9OtJLd/OzLapM1DLDv2g+0yyDdkLqSfZCaQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@ -97,11 +90,9 @@ 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/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
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=
@ -152,15 +143,12 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
github.com/envoyproxy/protoc-gen-validate v0.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.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
@ -262,9 +250,6 @@ github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -283,13 +268,10 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -305,7 +287,6 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
@ -356,8 +337,6 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -372,7 +351,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -388,8 +366,6 @@ github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:C
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.19.0 h1:JxYBTxUcXcOlCwLMuugc7Il0RMtJ7riaddqz6gG/ACA=
github.com/linode/linodego v0.19.0 h1:JxYBTxUcXcOlCwLMuugc7Il0RMtJ7riaddqz6gG/ACA=
github.com/linode/linodego v0.19.0/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
github.com/linode/linodego v0.19.0/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@ -440,7 +416,6 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
@ -452,16 +427,10 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae h1:cRqNH6AtRQwEqCpymMkaR2ePp08FBIYLkU7YusJeZJ8=
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae h1:cRqNH6AtRQwEqCpymMkaR2ePp08FBIYLkU7YusJeZJ8=
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92DrZBuWMNKcot1iZUHfbYSJyBWHGgg6Dn6s=
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92DrZBuWMNKcot1iZUHfbYSJyBWHGgg6Dn6s=
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
github.com/oracle/oci-go-sdk v21.4.0+incompatible h1:ORX+RXBuG/INBs+rgx6S3qoShEZ5+rwEEyRn2s6bPiw=
github.com/oracle/oci-go-sdk v21.4.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
@ -486,30 +455,23 @@ github.com/projectcontour/contour v1.5.0 h1:4zz4XWKKb1Nk2zXVQ27ZpoTivjG2DQvYLwrS
github.com/projectcontour/contour v1.5.0/go.mod h1:y1MEsorL/Q8lBG5BZz8Gzryi9L5ryVALOuHicmAdfW8=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@ -520,17 +482,17 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 h1:vOcHdR1nu7DO4BAx1rwzdHV7jQTzW3gqcBT5qxHSc6A=
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0/go.mod h1:FeplEtXXejBYC4NPAFTrs5L7KuK+5RL9bf5nB2vZe9o=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301 h1:qj0du14RIOnmePII/eTlw1aHKDYL6zxDIk/Dq7Tef9k=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200623155123-84df6c4b5301/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@ -571,7 +533,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I=
github.com/terra-farm/udnssdk v1.3.5/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/transip/gotransip v5.8.2+incompatible h1:aNJhw/w/3QBqFcHAIPz1ytoK5FexeMzbUCGrrhWr3H0=
@ -600,10 +561,7 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875 h1:C7kWARE8r64ppRadl40yfNo6pag+G6ocvGU2xZ6yNes=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
@ -631,7 +589,6 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -721,11 +678,8 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -846,15 +800,9 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
istio.io/api v0.0.0-20200529165953-72dad51d4ffc h1:cR9GmbIBAz3FnY3tgs1SRn/uiznhtvG+mZBfD1p2vIA=
istio.io/api v0.0.0-20200529165953-72dad51d4ffc h1:cR9GmbIBAz3FnY3tgs1SRn/uiznhtvG+mZBfD1p2vIA=
istio.io/api v0.0.0-20200529165953-72dad51d4ffc/go.mod h1:kyq3g5w42zl/AKlbzDGppYpGMQYMYMyZKeq0/eexML8=
istio.io/api v0.0.0-20200529165953-72dad51d4ffc/go.mod h1:kyq3g5w42zl/AKlbzDGppYpGMQYMYMyZKeq0/eexML8=
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751 h1:yH62fTmV+5l1XVTWcomsc1jjH/oH9u/tTgn5NVmdIac=
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751 h1:yH62fTmV+5l1XVTWcomsc1jjH/oH9u/tTgn5NVmdIac=
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751/go.mod h1:4SGvmmus5HNFdqQsIL+uQO1PbAhjQKtSjMTqwsvYHlg=
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751/go.mod h1:4SGvmmus5HNFdqQsIL+uQO1PbAhjQKtSjMTqwsvYHlg=
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs=
@ -866,7 +814,6 @@ k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
@ -875,41 +822,25 @@ k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY=
k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269 h1:d8Fm55A+7HOczX58+x9x+nJnJ1Devt1aCrWVIPaw/Vg=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ=
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=

View File

@ -58,6 +58,7 @@ import (
"sigs.k8s.io/external-dns/provider/rcode0"
"sigs.k8s.io/external-dns/provider/rdns"
"sigs.k8s.io/external-dns/provider/rfc2136"
"sigs.k8s.io/external-dns/provider/scaleway"
"sigs.k8s.io/external-dns/provider/transip"
"sigs.k8s.io/external-dns/provider/ultradns"
"sigs.k8s.io/external-dns/provider/vinyldns"
@ -289,6 +290,8 @@ func main() {
)
case "transip":
p, err = transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun)
case "scaleway":
p, err = scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun)
default:
log.Fatalf("unknown dns provider: %s", cfg.Provider)
}

View File

@ -317,7 +317,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, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "vultr", "ultradns")
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)

View File

@ -0,0 +1,53 @@
package scaleway
import (
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
"github.com/scaleway/scaleway-sdk-go/scw"
)
// DomainAPI is an interface matching the domain.API struct
type DomainAPI interface {
ListTasks(req *domain.ListTasksRequest, opts ...scw.RequestOption) (*domain.ListTasksResponse, error)
BuyDomain(req *domain.BuyDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error)
RenewDomain(req *domain.RenewDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error)
TransferDomain(req *domain.TransferDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error)
TradeDomain(req *domain.TradeDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error)
RegisterExternalDomain(req *domain.RegisterExternalDomainRequest, opts ...scw.RequestOption) (*domain.RegisterExternalDomainResponse, error)
DeleteExternalDomain(req *domain.DeleteExternalDomainRequest, opts ...scw.RequestOption) (*domain.DeleteExternalDomainResponse, error)
ListContacts(req *domain.ListContactsRequest, opts ...scw.RequestOption) (*domain.ListContactsResponse, error)
GetContact(req *domain.GetContactRequest, opts ...scw.RequestOption) (*domain.Contact, error)
UpdateContact(req *domain.UpdateContactRequest, opts ...scw.RequestOption) (*domain.Contact, error)
ListDomains(req *domain.ListDomainsRequest, opts ...scw.RequestOption) (*domain.ListDomainsResponse, error)
GetDomain(req *domain.GetDomainRequest, opts ...scw.RequestOption) (*domain.GetDomainResponse, error)
UpdateDomain(req *domain.UpdateDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error)
LockDomainTransfer(req *domain.LockDomainTransferRequest, opts ...scw.RequestOption) (*domain.Domain, error)
UnlockDomainTransfer(req *domain.UnlockDomainTransferRequest, opts ...scw.RequestOption) (*domain.Domain, error)
EnableDomainAutoRenew(req *domain.EnableDomainAutoRenewRequest, opts ...scw.RequestOption) (*domain.Domain, error)
DisableDomainAutoRenew(req *domain.DisableDomainAutoRenewRequest, opts ...scw.RequestOption) (*domain.Domain, error)
GetDomainAuthCode(req *domain.GetDomainAuthCodeRequest, opts ...scw.RequestOption) (*domain.GetDomainAuthCodeResponse, error)
EnableDomainDNSSEC(req *domain.EnableDomainDNSSECRequest, opts ...scw.RequestOption) (*domain.Domain, error)
DisableDomainDNSSEC(req *domain.DisableDomainDNSSECRequest, opts ...scw.RequestOption) (*domain.Domain, error)
CreateDNSZone(req *domain.CreateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error)
UpdateDNSZone(req *domain.UpdateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error)
CopyDNSZone(req *domain.CopyDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error)
DeleteDNSZone(req *domain.DeleteDNSZoneRequest, opts ...scw.RequestOption) (*domain.DeleteDNSZoneResponse, error)
ListDNSZoneNameservers(req *domain.ListDNSZoneNameserversRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneNameserversResponse, error)
UpdateDNSZoneNameservers(req *domain.UpdateDNSZoneNameserversRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneNameserversResponse, error)
ClearDNSZoneRecords(req *domain.ClearDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ClearDNSZoneRecordsResponse, error)
ExportRawDNSZone(req *domain.ExportRawDNSZoneRequest, opts ...scw.RequestOption) (*scw.File, error)
ImportRawDNSZone(req *domain.ImportRawDNSZoneRequest, opts ...scw.RequestOption) (*domain.ImportRawDNSZoneResponse, error)
ImportProviderDNSZone(req *domain.ImportProviderDNSZoneRequest, opts ...scw.RequestOption) (*domain.ImportProviderDNSZoneResponse, error)
RefreshDNSZone(req *domain.RefreshDNSZoneRequest, opts ...scw.RequestOption) (*domain.RefreshDNSZoneResponse, error)
ListDNSZoneVersions(req *domain.ListDNSZoneVersionsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneVersionsResponse, error)
ListDNSZoneVersionRecords(req *domain.ListDNSZoneVersionRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneVersionRecordsResponse, error)
GetDNSZoneVersionDiff(req *domain.GetDNSZoneVersionDiffRequest, opts ...scw.RequestOption) (*domain.GetDNSZoneVersionDiffResponse, error)
RestoreDNSZoneVersion(req *domain.RestoreDNSZoneVersionRequest, opts ...scw.RequestOption) (*domain.RestoreDNSZoneVersionResponse, error)
CreateSSLCertificate(req *domain.CreateSSLCertificateRequest, opts ...scw.RequestOption) (*domain.ZoneSSL, error)
ListSSLCertificates(req *domain.ListSSLCertificatesRequest, opts ...scw.RequestOption) (*domain.ListSSLCertificatesResponse, error)
DeleteSSLCertificate(req *domain.DeleteSSLCertificateRequest, opts ...scw.RequestOption) (*domain.DeleteSSLCertificateResponse, error)
GetDNSZoneTsigKey(req *domain.GetDNSZoneTsigKeyRequest, opts ...scw.RequestOption) (*domain.GetDNSZoneTsigKeyResponse, error)
DeleteDNSZoneTsigKey(req *domain.DeleteDNSZoneTsigKeyRequest, opts ...scw.RequestOption) error
ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error)
ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error)
UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error)
}

View File

@ -0,0 +1,341 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scaleway
import (
"context"
"fmt"
"os"
"strconv"
"strings"
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
"github.com/scaleway/scaleway-sdk-go/scw"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
const (
scalewyRecordTTL uint32 = 300
scalewayDefaultPriority uint32 = 0
scalewayPriorityKey string = "scw/priority"
)
// ScalewayProvider implements the DNS provider for Scaleway DNS
type ScalewayProvider struct {
provider.BaseProvider
domainAPI DomainAPI
dryRun bool
// only consider hosted zones managing domains ending in this suffix
domainFilter endpoint.DomainFilter
}
// ScalewayChange differentiates between ChangActions
type ScalewayChange struct {
Action string
Record []domain.Record
}
// NewScalewayProvider initializes a new Scaleway DNS provider
func NewScalewayProvider(ctx context.Context, domainFilter endpoint.DomainFilter, dryRun bool) (*ScalewayProvider, error) {
accessKey, ok := os.LookupEnv(scw.ScwAccessKeyEnv)
if !ok {
return nil, fmt.Errorf("environment variable %s not found", scw.ScwAccessKeyEnv)
}
secretKey, ok := os.LookupEnv(scw.ScwSecretKeyEnv)
if !ok {
return nil, fmt.Errorf("environment variable %s not found", scw.ScwSecretKeyEnv)
}
organizationID, ok := os.LookupEnv(scw.ScwDefaultOrganizationIDEnv)
if !ok {
return nil, fmt.Errorf("environment variable %s not found", scw.ScwDefaultOrganizationIDEnv)
}
scwClient, err := scw.NewClient(
scw.WithEnv(),
scw.WithAuth(accessKey, secretKey),
scw.WithDefaultOrganizationID(organizationID),
scw.WithUserAgent("ExternalDNS"),
)
if err != nil {
return nil, err
}
domainAPI := domain.NewAPI(scwClient)
return &ScalewayProvider{
domainAPI: domainAPI,
dryRun: dryRun,
domainFilter: domainFilter,
}, nil
}
// Zones returns the list of hosted zones.
func (p *ScalewayProvider) Zones(ctx context.Context) ([]*domain.DNSZone, error) {
res := []*domain.DNSZone{}
dnsZones, err := p.domainAPI.ListDNSZones(&domain.ListDNSZonesRequest{}, scw.WithAllPages(), scw.WithContext(ctx))
if err != nil {
return nil, err
}
for _, dnsZone := range dnsZones.DNSZones {
if p.domainFilter.Match(getCompleteZoneName(dnsZone)) {
res = append(res, dnsZone)
}
}
return res, nil
}
// Records returns the list of records in a given zone.
func (p *ScalewayProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
endpoints := map[string]*endpoint.Endpoint{}
dnsZones, err := p.Zones(ctx)
if err != nil {
return nil, err
}
for _, zone := range dnsZones {
recordsResp, err := p.domainAPI.ListDNSZoneRecords(&domain.ListDNSZoneRecordsRequest{
DNSZone: getCompleteZoneName(zone),
}, scw.WithAllPages())
if err != nil {
return nil, err
}
for _, record := range recordsResp.Records {
name := record.Name + "."
if record.Name == "" {
name = ""
}
// trim any leading or ending dot
fullRecordName := strings.Trim(name+getCompleteZoneName(zone), ".")
if !provider.SupportedRecordType(record.Type.String()) {
log.Infof("Skipping record %s because type %s is not supported", fullRecordName, record.Type.String())
continue
}
// in external DNS, same endpoint have the same ttl and same priority
// it's not the case in Scaleway DNS. It should never happen, but if
// the record is modified without going through ExternalDNS, we could have
// different priorities of ttls for a same name.
// In this case, we juste take the first one.
if existingEndpoint, ok := endpoints[record.Type.String()+"/"+fullRecordName]; ok {
existingEndpoint.Targets = append(existingEndpoint.Targets, record.Data)
log.Infof("Appending target %s to record %s, using TTL and priotiry of target %s", record.Data, fullRecordName, existingEndpoint.Targets[0])
} else {
ep := endpoint.NewEndpointWithTTL(fullRecordName, record.Type.String(), endpoint.TTL(record.TTL), record.Data)
ep = ep.WithProviderSpecific(scalewayPriorityKey, fmt.Sprintf("%d", record.Priority))
endpoints[record.Type.String()+"/"+fullRecordName] = ep
}
}
}
returnedEndpoints := []*endpoint.Endpoint{}
for _, ep := range endpoints {
returnedEndpoints = append(returnedEndpoints, ep)
}
return returnedEndpoints, nil
}
// ApplyChanges applies a set of changes in a zone.
func (p *ScalewayProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
requests, err := p.generateApplyRequests(ctx, changes)
if err != nil {
return err
}
for _, req := range requests {
logChanges(req)
if p.dryRun {
log.Info("Running in dry run mode")
continue
}
_, err := p.domainAPI.UpdateDNSZoneRecords(req, scw.WithContext(ctx))
if err != nil {
return err
}
}
return nil
}
func (p *ScalewayProvider) generateApplyRequests(ctx context.Context, changes *plan.Changes) ([]*domain.UpdateDNSZoneRecordsRequest, error) {
returnedRequests := []*domain.UpdateDNSZoneRecordsRequest{}
recordsToAdd := map[string]*domain.RecordChangeAdd{}
recordsToDelete := map[string][]*domain.RecordChange{}
dnsZones, err := p.Zones(ctx)
if err != nil {
return nil, err
}
zoneNameMapper := provider.ZoneIDName{}
for _, zone := range dnsZones {
zoneName := getCompleteZoneName(zone)
zoneNameMapper.Add(zoneName, zoneName)
recordsToAdd[zoneName] = &domain.RecordChangeAdd{
Records: []*domain.Record{},
}
recordsToDelete[zoneName] = []*domain.RecordChange{}
}
for _, c := range changes.UpdateOld {
zone, _ := zoneNameMapper.FindZone(c.DNSName)
if zone == "" {
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
continue
}
recordsToDelete[zone] = append(recordsToDelete[zone], endpointToScalewayRecordsChangeDelete(zone, c)...)
}
for _, c := range changes.Delete {
zone, _ := zoneNameMapper.FindZone(c.DNSName)
if zone == "" {
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
continue
}
recordsToDelete[zone] = append(recordsToDelete[zone], endpointToScalewayRecordsChangeDelete(zone, c)...)
}
for _, c := range changes.Create {
zone, _ := zoneNameMapper.FindZone(c.DNSName)
if zone == "" {
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
continue
}
recordsToAdd[zone].Records = append(recordsToAdd[zone].Records, endpointToScalewayRecords(zone, c)...)
}
for _, c := range changes.UpdateNew {
zone, _ := zoneNameMapper.FindZone(c.DNSName)
if zone == "" {
log.Infof("Ignore record %s since it's not handled by ExternalDNS", c.DNSName)
continue
}
recordsToAdd[zone].Records = append(recordsToAdd[zone].Records, endpointToScalewayRecords(zone, c)...)
}
for _, zone := range dnsZones {
zoneName := getCompleteZoneName(zone)
req := &domain.UpdateDNSZoneRecordsRequest{
DNSZone: zoneName,
Changes: recordsToDelete[zoneName],
}
req.Changes = append(req.Changes, &domain.RecordChange{
Add: recordsToAdd[zoneName],
})
returnedRequests = append(returnedRequests, req)
}
return returnedRequests, nil
}
func getCompleteZoneName(zone *domain.DNSZone) string {
subdomain := zone.Subdomain + "."
if zone.Subdomain == "" {
subdomain = ""
}
return subdomain + zone.Domain
}
func endpointToScalewayRecords(zoneName string, ep *endpoint.Endpoint) []*domain.Record {
// no annotation results in a TTL of 0, default to 300 for consistency with other providers
var ttl = scalewyRecordTTL
if ep.RecordTTL.IsConfigured() {
ttl = uint32(ep.RecordTTL)
}
var priority = scalewayDefaultPriority
if prop, ok := ep.GetProviderSpecificProperty(scalewayPriorityKey); ok {
prio, err := strconv.ParseUint(prop.Value, 10, 64)
if err != nil {
log.Errorf("Failed parsing value of %s: %s: %v; using priority of %d", scalewayPriorityKey, prop.Value, err, scalewayDefaultPriority)
} else {
priority = uint32(prio)
}
}
records := []*domain.Record{}
for _, target := range ep.Targets {
records = append(records, &domain.Record{
Data: target,
Name: strings.Trim(strings.TrimSuffix(ep.DNSName, zoneName), ". "),
Priority: priority,
TTL: ttl,
Type: domain.RecordType(ep.RecordType),
})
}
return records
}
func endpointToScalewayRecordsChangeDelete(zoneName string, ep *endpoint.Endpoint) []*domain.RecordChange {
records := []*domain.RecordChange{}
for _, target := range ep.Targets {
records = append(records, &domain.RecordChange{
Delete: &domain.RecordChangeDelete{
Data: target,
Name: strings.Trim(strings.TrimSuffix(ep.DNSName, zoneName), ". "),
Type: domain.RecordType(ep.RecordType),
},
})
}
return records
}
func logChanges(req *domain.UpdateDNSZoneRecordsRequest) {
log.Infof("Updating zone %s", req.DNSZone)
if !log.IsLevelEnabled(log.InfoLevel) {
return
}
for _, change := range req.Changes {
if change.Add != nil {
for _, add := range change.Add.Records {
name := add.Name + "."
if add.Name == "" {
name = ""
}
logFields := log.Fields{
"record": name + req.DNSZone,
"type": add.Type.String(),
"ttl": add.TTL,
"priority": add.Priority,
"data": add.Data,
}
log.WithFields(logFields).Info("Adding record")
}
} else if change.Delete != nil {
name := change.Delete.Name + "."
if change.Delete.Name == "" {
name = ""
}
logFields := log.Fields{
"record": name + req.DNSZone,
"type": change.Delete.Type.String(),
"data": change.Delete.Data,
}
log.WithFields(logFields).Info("Deleting record")
}
}
}

View File

@ -0,0 +1,623 @@
package scaleway
import (
"context"
"os"
"reflect"
"testing"
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2alpha2"
"github.com/scaleway/scaleway-sdk-go/scw"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
type mockScalewayDomain struct {
*domain.API
}
// Need to implement all these useless methods
func (m *mockScalewayDomain) ListTasks(req *domain.ListTasksRequest, opts ...scw.RequestOption) (*domain.ListTasksResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) BuyDomain(req *domain.BuyDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) RenewDomain(req *domain.RenewDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) TransferDomain(req *domain.TransferDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) TradeDomain(req *domain.TradeDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) RegisterExternalDomain(req *domain.RegisterExternalDomainRequest, opts ...scw.RequestOption) (*domain.RegisterExternalDomainResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) DeleteExternalDomain(req *domain.DeleteExternalDomainRequest, opts ...scw.RequestOption) (*domain.DeleteExternalDomainResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListContacts(req *domain.ListContactsRequest, opts ...scw.RequestOption) (*domain.ListContactsResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) GetContact(req *domain.GetContactRequest, opts ...scw.RequestOption) (*domain.Contact, error) {
return nil, nil
}
func (m *mockScalewayDomain) UpdateContact(req *domain.UpdateContactRequest, opts ...scw.RequestOption) (*domain.Contact, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListDomains(req *domain.ListDomainsRequest, opts ...scw.RequestOption) (*domain.ListDomainsResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) GetDomain(req *domain.GetDomainRequest, opts ...scw.RequestOption) (*domain.GetDomainResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) UpdateDomain(req *domain.UpdateDomainRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) LockDomainTransfer(req *domain.LockDomainTransferRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) UnlockDomainTransfer(req *domain.UnlockDomainTransferRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) EnableDomainAutoRenew(req *domain.EnableDomainAutoRenewRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) DisableDomainAutoRenew(req *domain.DisableDomainAutoRenewRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) GetDomainAuthCode(req *domain.GetDomainAuthCodeRequest, opts ...scw.RequestOption) (*domain.GetDomainAuthCodeResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) EnableDomainDNSSEC(req *domain.EnableDomainDNSSECRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) DisableDomainDNSSEC(req *domain.DisableDomainDNSSECRequest, opts ...scw.RequestOption) (*domain.Domain, error) {
return nil, nil
}
func (m *mockScalewayDomain) CreateDNSZone(req *domain.CreateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error) {
return nil, nil
}
func (m *mockScalewayDomain) UpdateDNSZone(req *domain.UpdateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error) {
return nil, nil
}
func (m *mockScalewayDomain) CopyDNSZone(req *domain.CopyDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error) {
return nil, nil
}
func (m *mockScalewayDomain) DeleteDNSZone(req *domain.DeleteDNSZoneRequest, opts ...scw.RequestOption) (*domain.DeleteDNSZoneResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListDNSZoneNameservers(req *domain.ListDNSZoneNameserversRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneNameserversResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) UpdateDNSZoneNameservers(req *domain.UpdateDNSZoneNameserversRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneNameserversResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ClearDNSZoneRecords(req *domain.ClearDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ClearDNSZoneRecordsResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ExportRawDNSZone(req *domain.ExportRawDNSZoneRequest, opts ...scw.RequestOption) (*scw.File, error) {
return nil, nil
}
func (m *mockScalewayDomain) ImportRawDNSZone(req *domain.ImportRawDNSZoneRequest, opts ...scw.RequestOption) (*domain.ImportRawDNSZoneResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ImportProviderDNSZone(req *domain.ImportProviderDNSZoneRequest, opts ...scw.RequestOption) (*domain.ImportProviderDNSZoneResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) RefreshDNSZone(req *domain.RefreshDNSZoneRequest, opts ...scw.RequestOption) (*domain.RefreshDNSZoneResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListDNSZoneVersions(req *domain.ListDNSZoneVersionsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneVersionsResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListDNSZoneVersionRecords(req *domain.ListDNSZoneVersionRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneVersionRecordsResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) GetDNSZoneVersionDiff(req *domain.GetDNSZoneVersionDiffRequest, opts ...scw.RequestOption) (*domain.GetDNSZoneVersionDiffResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) RestoreDNSZoneVersion(req *domain.RestoreDNSZoneVersionRequest, opts ...scw.RequestOption) (*domain.RestoreDNSZoneVersionResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) CreateSSLCertificate(req *domain.CreateSSLCertificateRequest, opts ...scw.RequestOption) (*domain.ZoneSSL, error) {
return nil, nil
}
func (m *mockScalewayDomain) ListSSLCertificates(req *domain.ListSSLCertificatesRequest, opts ...scw.RequestOption) (*domain.ListSSLCertificatesResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) DeleteSSLCertificate(req *domain.DeleteSSLCertificateRequest, opts ...scw.RequestOption) (*domain.DeleteSSLCertificateResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) GetDNSZoneTsigKey(req *domain.GetDNSZoneTsigKeyRequest, opts ...scw.RequestOption) (*domain.GetDNSZoneTsigKeyResponse, error) {
return nil, nil
}
func (m *mockScalewayDomain) DeleteDNSZoneTsigKey(req *domain.DeleteDNSZoneTsigKeyRequest, opts ...scw.RequestOption) error {
return nil
}
func (m *mockScalewayDomain) ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error) {
return &domain.ListDNSZonesResponse{
DNSZones: []*domain.DNSZone{
{
Domain: "example.com",
Subdomain: "",
},
{
Domain: "example.com",
Subdomain: "test",
},
{
Domain: "dummy.me",
Subdomain: "",
},
{
Domain: "dummy.me",
Subdomain: "test",
},
},
}, nil
}
func (m *mockScalewayDomain) ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error) {
records := []*domain.Record{}
if req.DNSZone == "example.com" {
records = []*domain.Record{
{
Data: "1.1.1.1",
Name: "one",
TTL: 300,
Priority: 0,
Type: domain.RecordTypeA,
},
{
Data: "1.1.1.2",
Name: "two",
TTL: 300,
Priority: 0,
Type: domain.RecordTypeA,
},
{
Data: "1.1.1.3",
Name: "two",
TTL: 300,
Priority: 0,
Type: domain.RecordTypeA,
},
}
} else if req.DNSZone == "test.example.com" {
records = []*domain.Record{
{
Data: "1.1.1.1",
Name: "",
TTL: 300,
Priority: 0,
Type: domain.RecordTypeA,
},
{
Data: "test.example.com",
Name: "two",
TTL: 600,
Priority: 30,
Type: domain.RecordTypeCNAME,
},
}
}
return &domain.ListDNSZoneRecordsResponse{
Records: records,
}, nil
}
func (m *mockScalewayDomain) UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) {
return nil, nil
}
func TestScalewayProvider_NewScalewayProvider(t *testing.T) {
_ = os.Setenv(scw.ScwAccessKeyEnv, "SCWXXXXXXXXXXXXXXXXX")
_ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111")
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "11111111-1111-1111-1111-111111111111")
_, err := NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err != nil {
t.Errorf("failed : %s", err)
}
_ = os.Unsetenv(scw.ScwDefaultOrganizationIDEnv)
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "dummy")
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
_ = os.Unsetenv(scw.ScwSecretKeyEnv)
_ = os.Setenv(scw.ScwDefaultOrganizationIDEnv, "11111111-1111-1111-1111-111111111111")
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
_ = os.Setenv(scw.ScwSecretKeyEnv, "dummy")
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
_ = os.Unsetenv(scw.ScwAccessKeyEnv)
_ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111")
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
_ = os.Setenv(scw.ScwAccessKeyEnv, "dummy")
_, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true)
if err == nil {
t.Errorf("expected to fail")
}
}
func TestScalewayProvider_Zones(t *testing.T) {
mocked := mockScalewayDomain{nil}
provider := &ScalewayProvider{
domainAPI: &mocked,
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
}
expected := []*domain.DNSZone{
{
Domain: "example.com",
Subdomain: "",
},
{
Domain: "example.com",
Subdomain: "test",
},
}
zones, err := provider.Zones(context.Background())
if err != nil {
t.Fatal(err)
}
require.Len(t, zones, len(expected))
for i, zone := range zones {
assert.Equal(t, expected[i], zone)
}
}
func TestScalewayProvider_Records(t *testing.T) {
mocked := mockScalewayDomain{nil}
provider := &ScalewayProvider{
domainAPI: &mocked,
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
}
expected := []*endpoint.Endpoint{
{
DNSName: "one.example.com",
RecordTTL: 300,
RecordType: "A",
Targets: []string{"1.1.1.1"},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "0",
},
},
},
{
DNSName: "two.example.com",
RecordTTL: 300,
RecordType: "A",
Targets: []string{"1.1.1.2", "1.1.1.3"},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "0",
},
},
},
{
DNSName: "test.example.com",
RecordTTL: 300,
RecordType: "A",
Targets: []string{"1.1.1.1"},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "0",
},
},
},
{
DNSName: "two.test.example.com",
RecordTTL: 600,
RecordType: "CNAME",
Targets: []string{"test.example.com"},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "30",
},
},
},
}
records, err := provider.Records(context.TODO())
if err != nil {
t.Fatal(err)
}
require.Len(t, records, len(expected))
for _, record := range records {
found := false
for _, expectedRecord := range expected {
if checkRecordEquality(record, expectedRecord) {
found = true
}
}
assert.Equal(t, true, found)
}
}
// this test is really ugly since we are working on maps, so array are randomly sorted
// feel free to modify if you have a better idea
func TestScalewayProvider_generateApplyRequests(t *testing.T) {
mocked := mockScalewayDomain{nil}
provider := &ScalewayProvider{
domainAPI: &mocked,
domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
}
expected := []*domain.UpdateDNSZoneRecordsRequest{
{
DNSZone: "example.com",
Changes: []*domain.RecordChange{
{
Add: &domain.RecordChangeAdd{
Records: []*domain.Record{
{
Data: "1.1.1.1",
Name: "",
TTL: 300,
Type: domain.RecordTypeA,
Priority: 0,
},
{
Data: "1.1.1.2",
Name: "",
TTL: 300,
Type: domain.RecordTypeA,
Priority: 0,
},
{
Data: "2.2.2.2",
Name: "me",
TTL: 600,
Type: domain.RecordTypeA,
Priority: 30,
},
},
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "3.3.3.3",
Name: "me",
Type: domain.RecordTypeA,
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "1.1.1.1",
Name: "here",
Type: domain.RecordTypeA,
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "1.1.1.2",
Name: "here",
Type: domain.RecordTypeA,
},
},
},
},
{
DNSZone: "test.example.com",
Changes: []*domain.RecordChange{
{
Add: &domain.RecordChangeAdd{
Records: []*domain.Record{
{
Data: "example.com",
Name: "",
TTL: 600,
Type: domain.RecordTypeCNAME,
Priority: 20,
},
{
Data: "1.2.3.4",
Name: "my",
TTL: 300,
Type: domain.RecordTypeA,
Priority: 0,
},
{
Data: "5.6.7.8",
Name: "my",
TTL: 300,
Type: domain.RecordTypeA,
Priority: 0,
},
},
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "1.1.1.1",
Name: "here.is.my",
Type: domain.RecordTypeA,
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "4.4.4.4",
Name: "my",
Type: domain.RecordTypeA,
},
},
{
Delete: &domain.RecordChangeDelete{
Data: "5.5.5.5",
Name: "my",
Type: domain.RecordTypeA,
},
},
},
},
}
changes := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "example.com",
RecordType: "A",
Targets: []string{"1.1.1.1", "1.1.1.2"},
},
{
DNSName: "test.example.com",
RecordType: "CNAME",
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "20",
},
},
RecordTTL: 600,
Targets: []string{"example.com"},
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "here.example.com",
RecordType: "A",
Targets: []string{"1.1.1.1", "1.1.1.2"},
},
{
DNSName: "here.is.my.test.example.com",
RecordType: "A",
Targets: []string{"1.1.1.1"},
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "me.example.com",
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "30",
},
},
RecordType: "A",
RecordTTL: 600,
Targets: []string{"2.2.2.2"},
},
{
DNSName: "my.test.example.com",
RecordType: "A",
Targets: []string{"1.2.3.4", "5.6.7.8"},
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "me.example.com",
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: scalewayPriorityKey,
Value: "1234",
},
},
RecordType: "A",
Targets: []string{"3.3.3.3"},
},
{
DNSName: "my.test.example.com",
RecordType: "A",
Targets: []string{"4.4.4.4", "5.5.5.5"},
},
},
}
requests, err := provider.generateApplyRequests(context.TODO(), changes)
if err != nil {
t.Fatal(err)
}
require.Len(t, requests, len(expected))
total := int(len(expected))
for _, req := range requests {
for _, exp := range expected {
if checkScalewayReqChanges(req, exp) {
total--
}
}
}
assert.Equal(t, 0, total)
}
func checkRecordEquality(record1, record2 *endpoint.Endpoint) bool {
return record1.Targets.Same(record2.Targets) &&
record1.DNSName == record2.DNSName &&
record1.RecordTTL == record2.RecordTTL &&
record1.RecordType == record2.RecordType &&
reflect.DeepEqual(record1.ProviderSpecific, record2.ProviderSpecific)
}
func checkScalewayReqChanges(r1, r2 *domain.UpdateDNSZoneRecordsRequest) bool {
if r1.DNSZone != r2.DNSZone {
return false
}
if len(r1.Changes) != len(r2.Changes) {
return false
}
total := int(len(r1.Changes))
for _, c1 := range r1.Changes {
for _, c2 := range r2.Changes {
// we only have 1 add per request
if c1.Add != nil && c2.Add != nil && checkScalewayRecords(c1.Add.Records, c2.Add.Records) {
total--
} else if c1.Delete != nil && c2.Delete != nil {
if c1.Delete.Data == c2.Delete.Data && c1.Delete.Name == c2.Delete.Name && c1.Delete.Type == c2.Delete.Type {
total--
}
}
}
}
return total == 0
}
func checkScalewayRecords(rs1, rs2 []*domain.Record) bool {
if len(rs1) != len(rs2) {
return false
}
total := int(len(rs1))
for _, r1 := range rs1 {
for _, r2 := range rs2 {
if r1.Data == r2.Data &&
r1.Name == r2.Name &&
r1.Priority == r2.Priority &&
r1.TTL == r2.TTL &&
r1.Type == r2.Type {
total--
}
}
}
return total == 0
}