Merge branch 'master' into master

This commit is contained in:
Kushal Bhandari 2020-06-30 11:18:58 -07:00 committed by GitHub
commit 3b085c5fcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 891 additions and 201 deletions

View File

@ -39,6 +39,7 @@ Providers
- [ ] Azure - [ ] Azure
- [ ] Cloudflare - [ ] Cloudflare
- [x] DigitalOcean - [x] DigitalOcean
- [x] DNSimple
- [x] Google - [x] Google
- [ ] InMemory - [ ] InMemory
- [x] Linode - [x] Linode
@ -61,6 +62,9 @@ This value is a constant in the provider code.
The DigitalOcean Provider overrides the value to 300s when the TTL is 0. The DigitalOcean Provider overrides the value to 300s when the TTL is 0.
This value is a constant in the provider code. This value is a constant in the provider code.
### DNSimple Provider
The DNSimple Provider default TTL is used when the TTL is 0. The default TTL is 3600s.
### Google Provider ### Google Provider
Previously with the Google Provider, TTL's were hard-coded to 300s. Previously with the Google Provider, TTL's were hard-coded to 300s.
For safety, the Google Provider overrides the value to 300s when the TTL is 0. For safety, the Google Provider overrides the value to 300s when the TTL is 0.

View File

@ -139,12 +139,12 @@ The following are relevant snippets from that tutorial.
#### Install a sample service #### Install a sample service
With automatic sidecar injection: With automatic sidecar injection:
```bash ```bash
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.0/samples/httpbin/httpbin.yaml $ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.6/samples/httpbin/httpbin.yaml
``` ```
Otherwise: Otherwise:
```bash ```bash
$ kubectl apply -f <(istioctl kube-inject -f https://raw.githubusercontent.com/istio/istio/release-1.0/samples/httpbin/httpbin.yaml) $ kubectl apply -f <(istioctl kube-inject -f https://raw.githubusercontent.com/istio/istio/release-1.6/samples/httpbin/httpbin.yaml)
``` ```
#### Create an Istio Gateway: #### Create an Istio Gateway:

9
go.mod
View File

@ -20,13 +20,14 @@ require (
github.com/cloudflare/cloudflare-go v0.10.1 github.com/cloudflare/cloudflare-go v0.10.1
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
github.com/digitalocean/godo v1.34.0 github.com/digitalocean/godo v1.36.0
github.com/dnsimple/dnsimple-go v0.60.0 github.com/dnsimple/dnsimple-go v0.60.0
github.com/exoscale/egoscale v0.18.1 github.com/exoscale/egoscale v0.18.1
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b // indirect github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b // indirect
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
github.com/google/go-cmp v0.4.1
github.com/gophercloud/gophercloud v0.1.0 github.com/gophercloud/gophercloud v0.1.0
github.com/gorilla/mux v1.7.4 // indirect github.com/gorilla/mux v1.7.4 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect
@ -57,11 +58,11 @@ require (
github.com/transip/gotransip v5.8.2+incompatible github.com/transip/gotransip v5.8.2+incompatible
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92 github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92
github.com/vultr/govultr v0.3.2 github.com/vultr/govultr v0.4.2
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875 go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875
go.uber.org/ratelimit v0.1.0 go.uber.org/ratelimit v0.1.0
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/api v0.15.0 google.golang.org/api v0.15.0
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8

25
go.sum
View File

@ -108,7 +108,6 @@ github.com/cloudflare/cloudflare-go v0.10.1 h1:d2CL6F9k2O0Ux0w27LgogJ5UOzZRj6a/h
github.com/cloudflare/cloudflare-go v0.10.1/go.mod h1:C0Y6eWnTJPMK2ceuOxx2pjh78UUHihcXeTTHb8r7QjU= github.com/cloudflare/cloudflare-go v0.10.1/go.mod h1:C0Y6eWnTJPMK2ceuOxx2pjh78UUHihcXeTTHb8r7QjU=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s= github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14= github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533 h1:8wZizuKuZVu5COB7EsBYxBQz8nRcXXn5d4Gt91eJLvU=
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@ -140,8 +139,8 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/digitalocean/godo v1.34.0 h1:OXJhLLJS2VTB5SziTyCq8valKVZ0uBHCFQsDW3/HF78= github.com/digitalocean/godo v1.36.0 h1:eRF8wNzHZyU7/wI3De/MQgiVSWdseDaf27bXj2gnOO0=
github.com/digitalocean/godo v1.34.0/go.mod h1:gfLm3JSupWD9V/ibQygXWW3IVz7hranzckH5UimhZsI= github.com/digitalocean/godo v1.36.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
@ -163,7 +162,6 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT
github.com/envoyproxy/go-control-plane v0.8.2/go.mod h1:EWRTAFN6uuDZIa6KOuUfrOMJ7ySgXZ44rVKiTWjKe34= github.com/envoyproxy/go-control-plane v0.8.2/go.mod h1:EWRTAFN6uuDZIa6KOuUfrOMJ7ySgXZ44rVKiTWjKe34=
github.com/envoyproxy/go-control-plane v0.9.0 h1:67WMNTvGrl7V1dWdKCeTwxDr7nio9clKoTlLhwIPnT4= github.com/envoyproxy/go-control-plane v0.9.0 h1:67WMNTvGrl7V1dWdKCeTwxDr7nio9clKoTlLhwIPnT4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.5 h1:lRJIqDD8yjV1YyPRqecMdytjDLs2fTXq363aCib5xPU=
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s= github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
github.com/envoyproxy/protoc-gen-validate v0.0.0-20190405222122-d6164de49109/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.0.0-20190405222122-d6164de49109/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@ -273,6 +271,8 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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/sync v0.0.0-20180314180146-1d60e4601c6f h1:kSqKc8ouCLIBHqdj9a9xxhtxlZhNqbePClixA4HoM44= github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f h1:kSqKc8ouCLIBHqdj9a9xxhtxlZhNqbePClixA4HoM44=
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:YCHYtYb9c8Q7XgYVYjmJBPtFPKx5QvOcPxHZWjldabE= github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:YCHYtYb9c8Q7XgYVYjmJBPtFPKx5QvOcPxHZWjldabE=
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -286,6 +286,8 @@ 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-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-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 v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
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-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@ -336,8 +338,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
@ -600,8 +602,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92 h1:Q76MzqJu++vAfhj0mVf7t0F4xHUbg+V/d/Uk5PBQjRU= github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92 h1:Q76MzqJu++vAfhj0mVf7t0F4xHUbg+V/d/Uk5PBQjRU=
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92/go.mod h1:AZuEfReFWdvtU0LatbLpo70t3lqdLvph2D5mqFP0bkA= github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92/go.mod h1:AZuEfReFWdvtU0LatbLpo70t3lqdLvph2D5mqFP0bkA=
github.com/vultr/govultr v0.3.2 h1:1tV/88jkm+4Y345qAXBe3peNbnmvCY/VAIZApklbKkI= github.com/vultr/govultr v0.4.2 h1:9i8xKZ+xp6vwZ9raqHoBLzhB4wCnMj7nOQTj5YIRLWY=
github.com/vultr/govultr v0.3.2/go.mod h1:81RwK1wAmb08alkFDJiZmu9gdv+IO+UamzaF0+PIieE= github.com/vultr/govultr v0.4.2/go.mod h1:TUuUizMOFc7z+PNMssb6iGjKjQfpw5arIaOLfocVudQ=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
@ -703,12 +705,15 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -750,6 +755,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/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/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -15,3 +15,6 @@ rules:
- apiGroups: [""] - apiGroups: [""]
resources: ["nodes"] resources: ["nodes"]
verbs: ["list"] verbs: ["list"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get","watch","list"]

View File

@ -112,7 +112,6 @@ func main() {
KubeConfig: cfg.KubeConfig, KubeConfig: cfg.KubeConfig,
KubeMaster: cfg.Master, KubeMaster: cfg.Master,
ServiceTypeFilter: cfg.ServiceTypeFilter, ServiceTypeFilter: cfg.ServiceTypeFilter,
IstioIngressGatewayServices: cfg.IstioIngressGatewayServices,
CFAPIEndpoint: cfg.CFAPIEndpoint, CFAPIEndpoint: cfg.CFAPIEndpoint,
CFUsername: cfg.CFUsername, CFUsername: cfg.CFUsername,
CFPassword: cfg.CFPassword, CFPassword: cfg.CFPassword,

View File

@ -41,7 +41,6 @@ type Config struct {
Master string Master string
KubeConfig string KubeConfig string
RequestTimeout time.Duration RequestTimeout time.Duration
IstioIngressGatewayServices []string
ContourLoadBalancerService string ContourLoadBalancerService string
SkipperRouteGroupVersion string SkipperRouteGroupVersion string
Sources []string Sources []string
@ -148,7 +147,6 @@ var defaultConfig = &Config{
Master: "", Master: "",
KubeConfig: "", KubeConfig: "",
RequestTimeout: time.Second * 30, RequestTimeout: time.Second * 30,
IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"},
ContourLoadBalancerService: "heptio-contour/contour", ContourLoadBalancerService: "heptio-contour/contour",
SkipperRouteGroupVersion: "zalando.org/v1", SkipperRouteGroupVersion: "zalando.org/v1",
Sources: nil, Sources: nil,

View File

@ -32,13 +32,6 @@ import (
) )
const ( const (
// DigitalOceanCreate is a ChangeAction enum value
DigitalOceanCreate = "CREATE"
// DigitalOceanDelete is a ChangeAction enum value
DigitalOceanDelete = "DELETE"
// DigitalOceanUpdate is a ChangeAction enum value
DigitalOceanUpdate = "UPDATE"
// digitalOceanRecordTTL is the default TTL value // digitalOceanRecordTTL is the default TTL value
digitalOceanRecordTTL = 300 digitalOceanRecordTTL = 300
) )
@ -54,10 +47,31 @@ type DigitalOceanProvider struct {
DryRun bool DryRun bool
} }
// DigitalOceanChange differentiates between ChangActions type digitalOceanChangeCreate struct {
type DigitalOceanChange struct { Domain string
Action string Options *godo.DomainRecordEditRequest
ResourceRecordSet godo.DomainRecord }
type digitalOceanChangeUpdate struct {
Domain string
DomainRecord godo.DomainRecord
Options *godo.DomainRecordEditRequest
}
type digitalOceanChangeDelete struct {
Domain string
RecordID int
}
// DigitalOceanChange contains all changes to apply to DNS
type digitalOceanChanges struct {
Creates []*digitalOceanChangeCreate
Updates []*digitalOceanChangeUpdate
Deletes []*digitalOceanChangeDelete
}
func (c *digitalOceanChanges) Empty() bool {
return len(c.Creates) == 0 && len(c.Updates) == 0 && len(c.Deletes) == 0
} }
// NewDigitalOceanProvider initializes a new DigitalOcean DNS based Provider. // NewDigitalOceanProvider initializes a new DigitalOcean DNS based Provider.
@ -71,13 +85,13 @@ func NewDigitalOceanProvider(ctx context.Context, domainFilter endpoint.DomainFi
})) }))
client := godo.NewClient(oauthClient) client := godo.NewClient(oauthClient)
provider := &DigitalOceanProvider{ p := &DigitalOceanProvider{
Client: client.Domains, Client: client.Domains,
domainFilter: domainFilter, domainFilter: domainFilter,
apiPageSize: apiPageSize, apiPageSize: apiPageSize,
DryRun: dryRun, DryRun: dryRun,
} }
return provider, nil return p, nil
} }
// Zones returns the list of hosted zones. // Zones returns the list of hosted zones.
@ -98,12 +112,45 @@ func (p *DigitalOceanProvider) Zones(ctx context.Context) ([]godo.Domain, error)
return result, nil return result, nil
} }
// Merge Endpoints with the same Name and Type into a single endpoint with multiple Targets.
func mergeEndpointsByNameType(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint {
endpointsByNameType := map[string][]*endpoint.Endpoint{}
for _, e := range endpoints {
key := fmt.Sprintf("%s-%s", e.DNSName, e.RecordType)
endpointsByNameType[key] = append(endpointsByNameType[key], e)
}
// If no merge occurred, just return the existing endpoints.
if len(endpointsByNameType) == len(endpoints) {
return endpoints
}
// Otherwise, construct a new list of endpoints with the endpoints merged.
var result []*endpoint.Endpoint
for _, endpoints := range endpointsByNameType {
dnsName := endpoints[0].DNSName
recordType := endpoints[0].RecordType
targets := make([]string, len(endpoints))
for i, e := range endpoints {
targets[i] = e.Targets[0]
}
e := endpoint.NewEndpoint(dnsName, recordType, targets...)
result = append(result, e)
}
return result
}
// Records returns the list of records in a given zone. // Records returns the list of records in a given zone.
func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
zones, err := p.Zones(ctx) zones, err := p.Zones(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
endpoints := []*endpoint.Endpoint{} endpoints := []*endpoint.Endpoint{}
for _, zone := range zones { for _, zone := range zones {
records, err := p.fetchRecords(ctx, zone.Name) records, err := p.fetchRecords(ctx, zone.Name)
@ -121,11 +168,21 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin
name = zone.Name name = zone.Name
} }
endpoints = append(endpoints, endpoint.NewEndpoint(name, r.Type, r.Data)) ep := endpoint.NewEndpoint(name, r.Type, r.Data)
endpoints = append(endpoints, ep)
} }
} }
} }
// Merge endpoints with the same name and type (e.g., multiple A records for a single
// DNS name) into one endpoint with multiple targets.
endpoints = mergeEndpointsByNameType(endpoints)
// Log the endpoints that were found.
log.WithFields(log.Fields{
"endpoints": endpoints,
}).Debug("Endpoints generated from DigitalOcean DNS")
return endpoints, nil return endpoints, nil
} }
@ -179,160 +236,379 @@ func (p *DigitalOceanProvider) fetchZones(ctx context.Context) ([]godo.Domain, e
return allZones, nil return allZones, nil
} }
// submitChanges takes a zone and a collection of Changes and sends them as a single transaction. func (p *DigitalOceanProvider) getRecordsByDomain(ctx context.Context) (map[string][]godo.DomainRecord, provider.ZoneIDName, error) {
func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes []*DigitalOceanChange) error { recordsByDomain := map[string][]godo.DomainRecord{}
zones, err := p.Zones(ctx)
if err != nil {
return nil, nil, err
}
zonesByDomain := make(map[string]godo.Domain)
zoneNameIDMapper := provider.ZoneIDName{}
for _, z := range zones {
zoneNameIDMapper.Add(z.Name, z.Name)
zonesByDomain[z.Name] = z
}
// Fetch records for each zone
for _, zone := range zones {
records, err := p.fetchRecords(ctx, zone.Name)
if err != nil {
return nil, nil, err
}
recordsByDomain[zone.Name] = append(recordsByDomain[zone.Name], records...)
}
return recordsByDomain, zoneNameIDMapper, nil
}
// Make a DomainRecordEditRequest that conforms to DigitalOcean API requirements:
// - Records at root of the zone have `@` as the name
// - CNAME records must end in a `.`
func makeDomainEditRequest(domain, name, recordType, data string, ttl int) *godo.DomainRecordEditRequest {
// Trim the domain off the name if present.
adjustedName := strings.TrimSuffix(name, "."+domain)
// Record at the root should be defined as @ instead of the full domain name.
if adjustedName == domain {
adjustedName = "@"
}
// For some reason the DO API requires the '.' at the end of "data" in case of CNAME request.
// Example: {"type":"CNAME","name":"hello","data":"www.example.com."}
if recordType == endpoint.RecordTypeCNAME && !strings.HasSuffix(data, ".") {
data += "."
}
return &godo.DomainRecordEditRequest{
Name: adjustedName,
Type: recordType,
Data: data,
TTL: ttl,
}
}
// submitChanges applies an instance of `digitalOceanChanges` to the DigitalOcean API.
func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes *digitalOceanChanges) error {
// return early if there is nothing to change // return early if there is nothing to change
if len(changes) == 0 { if changes.Empty() {
return nil return nil
} }
zones, err := p.Zones(ctx) for _, c := range changes.Creates {
log.WithFields(log.Fields{
"domain": c.Domain,
"dnsName": c.Options.Name,
"recordType": c.Options.Type,
"data": c.Options.Data,
"ttl": c.Options.TTL,
}).Debug("Creating domain record")
if p.DryRun {
continue
}
_, _, err := p.Client.CreateRecord(ctx, c.Domain, c.Options)
if err != nil {
return err
}
}
for _, u := range changes.Updates {
log.WithFields(log.Fields{
"domain": u.Domain,
"dnsName": u.Options.Name,
"recordType": u.Options.Type,
"data": u.Options.Data,
"ttl": u.Options.TTL,
}).Debug("Updating domain record")
if p.DryRun {
continue
}
_, _, err := p.Client.EditRecord(ctx, u.Domain, u.DomainRecord.ID, u.Options)
if err != nil {
return err
}
}
for _, d := range changes.Deletes {
log.WithFields(log.Fields{
"domain": d.Domain,
"recordId": d.RecordID,
}).Debug("Deleting domain record")
if p.DryRun {
continue
}
_, err := p.Client.DeleteRecord(ctx, d.Domain, d.RecordID)
if err != nil {
return err
}
}
return nil
}
func getTTLFromEndpoint(ep *endpoint.Endpoint) int {
if ep.RecordTTL.IsConfigured() {
return int(ep.RecordTTL)
}
return digitalOceanRecordTTL
}
func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint {
endpointsByZone := make(map[string][]*endpoint.Endpoint)
for _, ep := range endpoints {
zoneID, _ := zoneNameIDMapper.FindZone(ep.DNSName)
if zoneID == "" {
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", ep.DNSName)
continue
}
endpointsByZone[zoneID] = append(endpointsByZone[zoneID], ep)
}
return endpointsByZone
}
func getMatchingDomainRecords(records []godo.DomainRecord, domain string, ep *endpoint.Endpoint) []godo.DomainRecord {
var name string
if ep.DNSName != domain {
name = strings.TrimSuffix(ep.DNSName, "."+domain)
} else {
name = "@"
}
var result []godo.DomainRecord
for _, r := range records {
if r.Name == name && r.Type == ep.RecordType {
result = append(result, r)
}
}
return result
}
func processCreateActions(
recordsByDomain map[string][]godo.DomainRecord,
createsByDomain map[string][]*endpoint.Endpoint,
changes *digitalOceanChanges,
) error {
// Process endpoints that need to be created.
for domain, endpoints := range createsByDomain {
if len(endpoints) == 0 {
log.WithFields(log.Fields{
"domain": domain,
}).Debug("Skipping domain, no creates found.")
continue
}
records := recordsByDomain[domain]
for _, ep := range endpoints {
// Warn if there are existing records since we expect to create only new records.
matchingRecords := getMatchingDomainRecords(records, domain, ep)
if len(matchingRecords) > 0 {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
}).Warn("Preexisting records exist which should not exist for creation actions.")
}
ttl := getTTLFromEndpoint(ep)
for _, target := range ep.Targets {
changes.Creates = append(changes.Creates, &digitalOceanChangeCreate{
Domain: domain,
Options: makeDomainEditRequest(domain, ep.DNSName, ep.RecordType, target, ttl),
})
}
}
}
return nil
}
func processUpdateActions(
recordsByDomain map[string][]godo.DomainRecord,
updatesByDomain map[string][]*endpoint.Endpoint,
changes *digitalOceanChanges,
) error {
// Generate creates and updates based on existing
for domain, updates := range updatesByDomain {
if len(updates) == 0 {
log.WithFields(log.Fields{
"domain": domain,
}).Debug("Skipping Zone, no updates found.")
continue
}
records := recordsByDomain[domain]
log.WithFields(log.Fields{
"domain": domain,
"records": records,
}).Debug("Records for domain")
for _, ep := range updates {
matchingRecords := getMatchingDomainRecords(records, domain, ep)
log.WithFields(log.Fields{
"endpoint": ep,
"matchingRecords": matchingRecords,
}).Debug("matching records")
if len(matchingRecords) == 0 {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
}).Warn("Planning an update but no existing records found.")
}
matchingRecordsByTarget := map[string]godo.DomainRecord{}
for _, r := range matchingRecords {
matchingRecordsByTarget[r.Data] = r
}
ttl := getTTLFromEndpoint(ep)
// Generate create and delete actions based on existence of a record for each target.
for _, target := range ep.Targets {
if record, ok := matchingRecordsByTarget[target]; ok {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
"target": target,
}).Warn("Updating existing target")
changes.Updates = append(changes.Updates, &digitalOceanChangeUpdate{
Domain: domain,
DomainRecord: record,
Options: makeDomainEditRequest(domain, ep.DNSName, ep.RecordType, target, ttl),
})
delete(matchingRecordsByTarget, target)
} else {
// Record did not previously exist, create new 'target'
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
"target": target,
}).Warn("Creating new target")
changes.Creates = append(changes.Creates, &digitalOceanChangeCreate{
Domain: domain,
Options: makeDomainEditRequest(domain, ep.DNSName, ep.RecordType, target, ttl),
})
}
}
// Any remaining records have been removed, delete them
for _, record := range matchingRecordsByTarget {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
"target": record.Data,
}).Warn("Deleting target")
changes.Deletes = append(changes.Deletes, &digitalOceanChangeDelete{
Domain: domain,
RecordID: record.ID,
})
}
}
}
return nil
}
func processDeleteActions(
recordsByDomain map[string][]godo.DomainRecord,
deletesByDomain map[string][]*endpoint.Endpoint,
changes *digitalOceanChanges,
) error {
// Generate delete actions for each deleted endpoint.
for domain, deletes := range deletesByDomain {
if len(deletes) == 0 {
log.WithFields(log.Fields{
"domain": domain,
}).Debug("Skipping Zone, no deletes found.")
continue
}
records := recordsByDomain[domain]
for _, ep := range deletes {
matchingRecords := getMatchingDomainRecords(records, domain, ep)
if len(matchingRecords) == 0 {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": ep.DNSName,
"recordType": ep.RecordType,
}).Warn("Records to delete not found.")
}
for _, record := range matchingRecords {
doDelete := false
for _, t := range ep.Targets {
v1 := t
v2 := record.Data
if ep.RecordType == endpoint.RecordTypeCNAME {
v1 = strings.TrimSuffix(t, ".")
v2 = strings.TrimSuffix(t, ".")
}
if v1 == v2 {
doDelete = true
}
}
if doDelete {
changes.Deletes = append(changes.Deletes, &digitalOceanChangeDelete{
Domain: domain,
RecordID: record.ID,
})
}
}
}
}
return nil
}
// ApplyChanges applies the given set of generic changes to the provider.
func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, planChanges *plan.Changes) error {
// TODO: This should only retrieve zones affected by the given `planChanges`.
recordsByDomain, zoneNameIDMapper, err := p.getRecordsByDomain(ctx)
if err != nil { if err != nil {
return err return err
} }
// separate into per-zone change sets to be passed to the API. createsByDomain := endpointsByZone(zoneNameIDMapper, planChanges.Create)
changesByZone := digitalOceanChangesByZone(zones, changes) updatesByDomain := endpointsByZone(zoneNameIDMapper, planChanges.UpdateNew)
for zoneName, changes := range changesByZone { deletesByDomain := endpointsByZone(zoneNameIDMapper, planChanges.Delete)
records, err := p.fetchRecords(ctx, zoneName)
if err != nil {
log.Errorf("Failed to list records in the zone: %s", zoneName)
continue
}
for _, change := range changes {
logFields := log.Fields{
"record": change.ResourceRecordSet.Name,
"type": change.ResourceRecordSet.Type,
"ttl": change.ResourceRecordSet.TTL,
"action": change.Action,
"zone": zoneName,
}
log.WithFields(logFields).Info("Changing record.") var changes digitalOceanChanges
if p.DryRun { if err := processCreateActions(recordsByDomain, createsByDomain, &changes); err != nil {
continue return err
}
change.ResourceRecordSet.Name = strings.TrimSuffix(change.ResourceRecordSet.Name, "."+zoneName)
// record at the root should be defined as @ instead of
// the full domain name
if change.ResourceRecordSet.Name == zoneName {
change.ResourceRecordSet.Name = "@"
}
// for some reason the DO API requires the '.' at the end of "data" in case of CNAME request
// Example: {"type":"CNAME","name":"hello","data":"www.example.com."}
if change.ResourceRecordSet.Type == endpoint.RecordTypeCNAME {
change.ResourceRecordSet.Data += "."
}
switch change.Action {
case DigitalOceanCreate:
_, _, err = p.Client.CreateRecord(ctx, zoneName,
&godo.DomainRecordEditRequest{
Data: change.ResourceRecordSet.Data,
Name: change.ResourceRecordSet.Name,
Type: change.ResourceRecordSet.Type,
TTL: change.ResourceRecordSet.TTL,
})
if err != nil {
return err
}
case DigitalOceanDelete:
recordID := p.getRecordID(records, change.ResourceRecordSet)
_, err = p.Client.DeleteRecord(ctx, zoneName, recordID)
if err != nil {
return err
}
case DigitalOceanUpdate:
recordID := p.getRecordID(records, change.ResourceRecordSet)
_, _, err = p.Client.EditRecord(ctx, zoneName, recordID,
&godo.DomainRecordEditRequest{
Data: change.ResourceRecordSet.Data,
Name: change.ResourceRecordSet.Name,
Type: change.ResourceRecordSet.Type,
TTL: change.ResourceRecordSet.TTL,
})
if err != nil {
return err
}
}
}
} }
return nil
} if err := processUpdateActions(recordsByDomain, updatesByDomain, &changes); err != nil {
return err
// ApplyChanges applies a given set of changes in a given zone. }
func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
combinedChanges := make([]*DigitalOceanChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) if err := processDeleteActions(recordsByDomain, deletesByDomain, &changes); err != nil {
return err
combinedChanges = append(combinedChanges, newDigitalOceanChanges(DigitalOceanCreate, changes.Create)...) }
combinedChanges = append(combinedChanges, newDigitalOceanChanges(DigitalOceanUpdate, changes.UpdateNew)...)
combinedChanges = append(combinedChanges, newDigitalOceanChanges(DigitalOceanDelete, changes.Delete)...) return p.submitChanges(ctx, &changes)
return p.submitChanges(ctx, combinedChanges)
}
// newDigitalOceanChanges returns a collection of Changes based on the given records and action.
func newDigitalOceanChanges(action string, endpoints []*endpoint.Endpoint) []*DigitalOceanChange {
changes := make([]*DigitalOceanChange, 0, len(endpoints))
for _, endpoint := range endpoints {
changes = append(changes, newDigitalOceanChange(action, endpoint))
}
return changes
}
func newDigitalOceanChange(action string, endpoint *endpoint.Endpoint) *DigitalOceanChange {
// no annotation results in a TTL of 0, default to 300 for consistency with other providers
var ttl = digitalOceanRecordTTL
if endpoint.RecordTTL.IsConfigured() {
ttl = int(endpoint.RecordTTL)
}
change := &DigitalOceanChange{
Action: action,
ResourceRecordSet: godo.DomainRecord{
Name: endpoint.DNSName,
Type: endpoint.RecordType,
Data: endpoint.Targets[0],
TTL: ttl,
},
}
return change
}
// getRecordID returns the ID from a record.
// the ID is mandatory to update and delete records
func (p *DigitalOceanProvider) getRecordID(records []godo.DomainRecord, record godo.DomainRecord) int {
for _, zoneRecord := range records {
if zoneRecord.Name == record.Name && zoneRecord.Type == record.Type {
return zoneRecord.ID
}
}
return 0
}
// digitalOceanchangesByZone separates a multi-zone change into a single change per zone.
func digitalOceanChangesByZone(zones []godo.Domain, changeSet []*DigitalOceanChange) map[string][]*DigitalOceanChange {
changes := make(map[string][]*DigitalOceanChange)
zoneNameIDMapper := provider.ZoneIDName{}
for _, z := range zones {
zoneNameIDMapper.Add(z.Name, z.Name)
changes[z.Name] = []*DigitalOceanChange{}
}
for _, c := range changeSet {
zone, _ := zoneNameIDMapper.FindZone(c.ResourceRecordSet.Name)
if zone == "" {
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSet.Name)
continue
}
changes[zone] = append(changes[zone], c)
}
return changes
} }

View File

@ -20,9 +20,12 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"reflect"
"sort"
"testing" "testing"
"github.com/digitalocean/godo" "github.com/digitalocean/godo"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -143,10 +146,111 @@ func (m *mockDigitalOceanRecordsFail) Records(ctx context.Context, domain string
return []godo.DomainRecord{}, nil, fmt.Errorf("Failed to get records") return []godo.DomainRecord{}, nil, fmt.Errorf("Failed to get records")
} }
func TestNewDigitalOceanChanges(t *testing.T) { func isEmpty(xs interface{}) bool {
action := DigitalOceanCreate if xs != nil {
endpoints := []*endpoint.Endpoint{{DNSName: "new", Targets: endpoint.Targets{"target"}}} objValue := reflect.ValueOf(xs)
_ = newDigitalOceanChanges(action, endpoints) return objValue.Len() == 0
}
return true
}
// This function is an adapted copy of the testify package's ElementsMatch function with the
// call to ObjectsAreEqual replaced with cmp.Equal which better handles struct's with pointers to
// other structs. It also ignores ordering when comparing unlike cmp.Equal.
func elementsMatch(t *testing.T, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
if listA == nil && listB == nil {
return true
} else if listA == nil {
return isEmpty(listB)
} else if listB == nil {
return isEmpty(listA)
}
aKind := reflect.TypeOf(listA).Kind()
bKind := reflect.TypeOf(listB).Kind()
if aKind != reflect.Array && aKind != reflect.Slice {
return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
}
if bKind != reflect.Array && bKind != reflect.Slice {
return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
}
aValue := reflect.ValueOf(listA)
bValue := reflect.ValueOf(listB)
aLen := aValue.Len()
bLen := bValue.Len()
if aLen != bLen {
return assert.Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
}
// Mark indexes in bValue that we already used
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
element := aValue.Index(i).Interface()
found := false
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if cmp.Equal(bValue.Index(j).Interface(), element) {
visited[j] = true
found = true
break
}
}
if !found {
return assert.Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
}
}
return true
}
// Test adapted from test in testify library.
// https://github.com/stretchr/testify/blob/b8f7d52a4a7c581d5ed42333572e7fb857c687c2/assert/assertions_test.go#L768-L796
func TestElementsMatch(t *testing.T) {
mockT := new(testing.T)
cases := []struct {
expected interface{}
actual interface{}
result bool
}{
// matching
{nil, nil, true},
{nil, nil, true},
{[]int{}, []int{}, true},
{[]int{1}, []int{1}, true},
{[]int{1, 1}, []int{1, 1}, true},
{[]int{1, 2}, []int{1, 2}, true},
{[]int{1, 2}, []int{2, 1}, true},
{[2]int{1, 2}, [2]int{2, 1}, true},
{[]string{"hello", "world"}, []string{"world", "hello"}, true},
{[]string{"hello", "hello"}, []string{"hello", "hello"}, true},
{[]string{"hello", "hello", "world"}, []string{"hello", "world", "hello"}, true},
{[3]string{"hello", "hello", "world"}, [3]string{"hello", "world", "hello"}, true},
{[]int{}, nil, true},
// not matching
{[]int{1}, []int{1, 1}, false},
{[]int{1, 2}, []int{2, 2}, false},
{[]string{"hello", "hello"}, []string{"hello"}, false},
}
for _, c := range cases {
t.Run(fmt.Sprintf("ElementsMatch(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
res := elementsMatch(mockT, c.actual, c.expected)
if res != c.result {
t.Errorf("elementsMatch(%#v, %#v) should return %v", c.actual, c.expected, c.result)
}
})
}
} }
func TestDigitalOceanZones(t *testing.T) { func TestDigitalOceanZones(t *testing.T) {
@ -165,6 +269,38 @@ func TestDigitalOceanZones(t *testing.T) {
}) })
} }
func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
// Ensure that records at the root of the zone get `@` as the name.
r1 := makeDomainEditRequest("example.com", "example.com", endpoint.RecordTypeA,
"1.2.3.4", digitalOceanRecordTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeA,
Name: "@",
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
}, r1)
// Ensure the CNAME records have a `.` appended.
r2 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com", digitalOceanRecordTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME,
Name: "foo",
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
}, r2)
// Ensure that CNAME records do not have an extra `.` appended if they already have a `.`
r3 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com.", digitalOceanRecordTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME,
Name: "foo",
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
}, r3)
}
func TestDigitalOceanApplyChanges(t *testing.T) { func TestDigitalOceanApplyChanges(t *testing.T) {
changes := &plan.Changes{} changes := &plan.Changes{}
provider := &DigitalOceanProvider{ provider := &DigitalOceanProvider{
@ -185,6 +321,198 @@ func TestDigitalOceanApplyChanges(t *testing.T) {
} }
} }
func TestDigitalOceanProcessCreateActions(t *testing.T) {
recordsByDomain := map[string][]godo.DomainRecord{
"example.com": nil,
}
createsByDomain := map[string][]*endpoint.Endpoint{
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
},
}
var changes digitalOceanChanges
err := processCreateActions(recordsByDomain, createsByDomain, &changes)
require.NoError(t, err)
assert.Equal(t, 2, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 0, len(changes.Deletes))
expectedCreates := []*digitalOceanChangeCreate{
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
},
},
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
assert.Failf(t, "diff: %s", cmp.Diff(expectedCreates, changes.Creates))
}
}
func TestDigitalOceanProcessUpdateActions(t *testing.T) {
recordsByDomain := map[string][]godo.DomainRecord{
"example.com": {
{
ID: 1,
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
},
{
ID: 2,
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
TTL: digitalOceanRecordTTL,
},
{
ID: 3,
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
},
},
}
updatesByDomain := map[string][]*endpoint.Endpoint{
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "10.11.12.13"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "bar.example.com"),
},
}
var changes digitalOceanChanges
err := processUpdateActions(recordsByDomain, updatesByDomain, &changes)
require.NoError(t, err)
assert.Equal(t, 2, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 3, len(changes.Deletes))
expectedCreates := []*digitalOceanChangeCreate{
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "10.11.12.13",
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
},
},
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
assert.Failf(t, "diff: %s", cmp.Diff(expectedCreates, changes.Creates))
}
expectedDeletes := []*digitalOceanChangeDelete{
{
Domain: "example.com",
RecordID: 1,
},
{
Domain: "example.com",
RecordID: 2,
},
{
Domain: "example.com",
RecordID: 3,
},
}
if !elementsMatch(t, expectedDeletes, changes.Deletes) {
assert.Failf(t, "diff: %s", cmp.Diff(expectedDeletes, changes.Deletes))
}
}
func TestDigitalOceanProcessDeleteActions(t *testing.T) {
recordsByDomain := map[string][]godo.DomainRecord{
"example.com": {
{
ID: 1,
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
},
// This record will not be deleted because it represents a target not specified to be deleted.
{
ID: 2,
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
TTL: digitalOceanRecordTTL,
},
{
ID: 3,
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
},
},
}
deletesByDomain := map[string][]*endpoint.Endpoint{
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
},
}
var changes digitalOceanChanges
err := processDeleteActions(recordsByDomain, deletesByDomain, &changes)
require.NoError(t, err)
assert.Equal(t, 0, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 2, len(changes.Deletes))
expectedDeletes := []*digitalOceanChangeDelete{
{
Domain: "example.com",
RecordID: 1,
},
{
Domain: "example.com",
RecordID: 3,
},
}
if !elementsMatch(t, expectedDeletes, changes.Deletes) {
assert.Failf(t, "diff: %s", cmp.Diff(expectedDeletes, changes.Deletes))
}
}
func TestNewDigitalOceanProvider(t *testing.T) { func TestNewDigitalOceanProvider(t *testing.T) {
_ = os.Setenv("DO_TOKEN", "xxxxxxxxxxxxxxxxx") _ = os.Setenv("DO_TOKEN", "xxxxxxxxxxxxxxxxx")
_, err := NewDigitalOceanProvider(context.Background(), endpoint.NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true, 50) _, err := NewDigitalOceanProvider(context.Background(), endpoint.NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true, 50)
@ -198,29 +526,62 @@ func TestNewDigitalOceanProvider(t *testing.T) {
} }
} }
func TestDigitalOceanGetRecordID(t *testing.T) { func TestDigitalOceanGetMatchingDomainRecords(t *testing.T) {
p := &DigitalOceanProvider{}
records := []godo.DomainRecord{ records := []godo.DomainRecord{
{ {
ID: 1, ID: 1,
Name: "foo.com", Name: "foo",
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Data: "baz.org.",
}, },
{ {
ID: 2, ID: 2,
Name: "baz.de", Name: "baz",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
},
{
ID: 3,
Name: "baz",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
},
{
ID: 4,
Name: "@",
Type: endpoint.RecordTypeA,
Data: "9.10.11.12",
}, },
} }
assert.Equal(t, 1, p.getRecordID(records, godo.DomainRecord{
Name: "foo.com",
Type: endpoint.RecordTypeCNAME,
}))
assert.Equal(t, 0, p.getRecordID(records, godo.DomainRecord{ ep1 := endpoint.NewEndpoint("foo.com", endpoint.RecordTypeCNAME)
Name: "foo.com", assert.Equal(t, 1, len(getMatchingDomainRecords(records, "com", ep1)))
Type: endpoint.RecordTypeA,
})) ep2 := endpoint.NewEndpoint("foo.com", endpoint.RecordTypeA)
assert.Equal(t, 0, len(getMatchingDomainRecords(records, "com", ep2)))
ep3 := endpoint.NewEndpoint("baz.org", endpoint.RecordTypeA)
r := getMatchingDomainRecords(records, "org", ep3)
assert.Equal(t, 2, len(r))
assert.ElementsMatch(t, r, []godo.DomainRecord{
{
ID: 2,
Name: "baz",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
},
{
ID: 3,
Name: "baz",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
},
})
ep4 := endpoint.NewEndpoint("example.com", endpoint.RecordTypeA)
r2 := getMatchingDomainRecords(records, "example.com", ep4)
assert.Equal(t, 1, len(r2))
assert.Equal(t, "9.10.11.12", r2[0].Data)
} }
func validateDigitalOceanZones(t *testing.T, zones []godo.Domain, expected []godo.Domain) { func validateDigitalOceanZones(t *testing.T, zones []godo.Domain, expected []godo.Domain) {
@ -265,3 +626,36 @@ func TestDigitalOceanAllRecords(t *testing.T) {
t.Errorf("expected to fail, %s", err) t.Errorf("expected to fail, %s", err)
} }
} }
func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
xs := []*endpoint.Endpoint{
endpoint.NewEndpoint("foo.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("bar.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", "A", "5.6.7.8"),
endpoint.NewEndpoint("foo.example.com", "CNAME", "somewhere.out.there.com"),
}
merged := mergeEndpointsByNameType(xs)
assert.Equal(t, 3, len(merged))
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].DNSName != merged[j].DNSName {
return merged[i].DNSName < merged[j].DNSName
}
return merged[i].RecordType < merged[j].RecordType
})
assert.Equal(t, "bar.example.com", merged[0].DNSName)
assert.Equal(t, "A", merged[0].RecordType)
assert.Equal(t, 1, len(merged[0].Targets))
assert.Equal(t, "1.2.3.4", merged[0].Targets[0])
assert.Equal(t, "foo.example.com", merged[1].DNSName)
assert.Equal(t, "A", merged[1].RecordType)
assert.Equal(t, 2, len(merged[1].Targets))
assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[1].Targets)
assert.Equal(t, "foo.example.com", merged[2].DNSName)
assert.Equal(t, "CNAME", merged[2].RecordType)
assert.Equal(t, 1, len(merged[2].Targets))
assert.Equal(t, "somewhere.out.there.com", merged[2].Targets[0])
}

View File

@ -157,16 +157,18 @@ func (p *VultrProvider) submitChanges(ctx context.Context, changes []*VultrChang
for zoneName, changes := range zoneChanges { for zoneName, changes := range zoneChanges {
for _, change := range changes { for _, change := range changes {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"record": change.ResourceRecordSet.Name, "record": change.ResourceRecordSet.Name,
"type": change.ResourceRecordSet.Type, "type": change.ResourceRecordSet.Type,
"ttl": change.ResourceRecordSet.TTL, "ttl": change.ResourceRecordSet.TTL,
"action": change.Action, "priority": change.ResourceRecordSet.Priority,
"zone": zoneName, "action": change.Action,
"zone": zoneName,
}).Info("Changing record.") }).Info("Changing record.")
switch change.Action { switch change.Action {
case vultrCreate: case vultrCreate:
err = p.client.DNSRecord.Create(ctx, zoneName, change.ResourceRecordSet.Type, change.ResourceRecordSet.Name, change.ResourceRecordSet.Data, change.ResourceRecordSet.TTL, change.ResourceRecordSet.Priority) priority := getPriority(change.ResourceRecordSet.Priority)
err = p.client.DNSRecord.Create(ctx, zoneName, change.ResourceRecordSet.Type, change.ResourceRecordSet.Name, change.ResourceRecordSet.Data, change.ResourceRecordSet.TTL, priority)
if err != nil { if err != nil {
return err return err
} }
@ -276,3 +278,11 @@ func (p *VultrProvider) getRecordID(ctx context.Context, zone string, record gov
return 0, fmt.Errorf("no record was found") return 0, fmt.Errorf("no record was found")
} }
func getPriority(priority *int) int {
p := 0
if priority != nil {
p = *priority
}
return p
}

View File

@ -55,7 +55,6 @@ type Config struct {
KubeConfig string KubeConfig string
KubeMaster string KubeMaster string
ServiceTypeFilter []string ServiceTypeFilter []string
IstioIngressGatewayServices []string
CFAPIEndpoint string CFAPIEndpoint string
CFUsername string CFUsername string
CFPassword string CFPassword string

View File

@ -155,6 +155,5 @@ func TestByNames(t *testing.T) {
} }
var minimalConfig = &Config{ var minimalConfig = &Config{
IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"},
ContourLoadBalancerService: "heptio-contour/contour", ContourLoadBalancerService: "heptio-contour/contour",
} }