Unverified Commit 6f7361fd authored by Nick Jüttner's avatar Nick Jüttner Committed by GitHub
Browse files

Merge pull request #873 from njuettner/pagination-cloudflare-zones

Pagination cloudflare zones
parents 38fcaaae 8810311c
Showing with 308 additions and 290 deletions
+308 -290
This diff is collapsed.
......@@ -116,6 +116,10 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"]
name = "github.com/miekg/dns"
version = "1.0.8"
[[constraint]]
name = "github.com/google/go-cmp"
version = "0.2.0"
[[constraint]]
name = "github.com/sanyu/dynectsoap"
branch = "master"
......@@ -10,7 +10,6 @@
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/
linki
njuettner
hjacobs
raffo
......@@ -14,7 +14,11 @@ Here is typical example of [CRD API type](https://github.com/kubernetes-incubato
```go
type TTL int64
type Targets []string
type ProviderSpecific map[string]string
type ProviderSpecificProperty struct {
Name string
Value string
}
type ProviderSpecific []ProviderSpecificProperty
type Endpoint struct {
// The hostname of the DNS record
......
......@@ -33,7 +33,14 @@ spec:
labels:
type: object
providerSpecific:
type: object
items:
properties:
name:
type: string
value:
type: string
type: object
type: array
recordTTL:
format: int64
type: integer
......
......@@ -243,3 +243,11 @@ To do this with ExternalDNS you can use the `--annotation-filter` to specificall
an instance of a ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external`
then you can start two ExternalDNS providers one with `--annotation-filter=kubernetes.io/ingress.class=nginx-internal`
and one with `--annotation-filter=kubernetes.io/ingress.class=nginx-external`.
### Can external-dns manage(add/remove) records in a hosted zone which is setup in different aws account.
yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-incubator/external-dns/pull/524#issue-181256561
### how do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname`
separate them by `,`
......@@ -47,14 +47,14 @@ $ aws route53 create-hosted-zone --name "external-dns-test.my-org.com." --caller
Make a note of the ID of the hosted zone you just created.
```console
$ aws route53 list-hosted-zones-by-name --dns-name "external-dns-test.my-org.com." | jq -r '.HostedZones[0].Id'
$ aws route53 list-hosted-zones-by-name --output json --dns-name "external-dns-test.my-org.com." | jq -r '.HostedZones[0].Id'
/hostedzone/ZEWFWZ4R16P7IB
```
Make a note of the nameservers that were assigned to your new zone.
```console
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \
$ aws route53 list-resource-record-sets --output json --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \
--query "ResourceRecordSets[?Type == 'NS']" | jq -r '.[0].ResourceRecords[].Value'
ns-5514.awsdns-53.org.
...
......@@ -177,7 +177,7 @@ Annotations which are specific to AWS.
### alias
`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well.
`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. To make the target an alias, the ingress needs to be configured correctly as described in [the docs](./nginx-ingress.md#with-a-separate-tcp-load-balancer).
## Verify ExternalDNS works (Ingress example)
......@@ -247,7 +247,7 @@ spec:
After roughly two minutes check that a corresponding DNS record for your service was created.
```console
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \
$ aws route53 list-resource-record-sets --output json --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \
--query "ResourceRecordSets[?Name == 'nginx.external-dns-test.my-org.com.']|[?Type == 'A']"
[
{
......
......@@ -61,13 +61,18 @@ The `resourceGroup` is the Resource Group created in a previous step.
The `aadClientID` and `aaClientSecret` are assoiated with the Service Principal, that you need to create next.
### Creating service principal
A Service Principal with a minimum access level of contribute to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps.
A Service Principal with a minimum access level of `contributor` to the DNS zone(s) and `reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `contributor` to the resource group or the whole subscription).
This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps.
``` bash
> az login
```
>az login
...
# find the relevant subscription and set the az context. id = subscriptionId value in the azure.json.
>az account list
Find the relevant subscription and make sure it is selected (the same subscriptionId should be set into azure.json)
``` bash
> az account list
{
"cloudName": "AzureCloud",
"id": "<subscriptionId GUID>",
......@@ -79,22 +84,48 @@ A Service Principal with a minimum access level of contribute to the resource gr
"name": "name",
"type": "user"
}
>az account set -s id
# select the subscription
> az account set -s <subscriptionId GUID>
...
>az group show --name externaldns
```
Create the service principal
``` bash
> az ad sp create-for-rbac -n ExternalDnsServicePrincipal
{
"appId": "appId GUID", <-- aadClientId value
...
"password": "password", <-- aadClientSecret value
"tenant": "AzureAD Tenant Id" <-- tenantId value
}
```
Assign the rights for the service principal
```
# find out the resource ids of the resource group where the dns zone is deployed, and the dns zone itself
> az group show --name externaldns
{
"id": "/subscriptions/id/resourceGroups/externaldns",
...
}
# use the id from the previous step in the scopes argument
>az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/id/resourceGroups/externaldns" -n ExternalDnsServicePrincipal
> az network dns zone show --name example.com -g externaldns
{
"appId": "appId GUID", <-- aadClientId value
"id": "/subscriptions/.../resourceGroups/externaldns/providers/Microsoft.Network/dnszones/example.com",
...
"password": "password", <-- aadClientSecret value
"tenant": "AzureAD Tenant Id" <-- tenantId value
}
```
```
# assign the rights to the created service principal, using the resource ids from previous step
# 1. as a reader to the resource group
> az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
# 2. as a contributor to DNS Zone itself
> az role assignment create --role "Contributor" --assignee <appId GUID> --scope <dns zone resource id>
```
Now you can create a file named 'azure.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret:
......
......@@ -196,3 +196,7 @@ Now that we have verified that ExternalDNS will automatically manage Cloudflare
$ kubectl delete -f nginx.yaml
$ kubectl delete -f externaldns.yaml
```
## Setting cloudflare-proxied on a per-ingress basis
Using the `external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"` annotation on your ingress, you can specify if the proxy feature of Cloudflare should be enabled for that record. This setting will override the global `--cloudflare-proxied` setting.
......@@ -109,8 +109,14 @@ func (t Targets) IsLess(o Targets) bool {
return false
}
// ProviderSpecificProperty holds the name and value of a configuration which is specific to individual DNS providers
type ProviderSpecificProperty struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
}
// ProviderSpecific holds configuration which is specific to individual DNS providers
type ProviderSpecific map[string]string
type ProviderSpecific []ProviderSpecificProperty
// Endpoint is a high-level way of a connection between a service and an IP
type Endpoint struct {
......@@ -160,10 +166,21 @@ func (e *Endpoint) WithProviderSpecific(key, value string) *Endpoint {
if e.ProviderSpecific == nil {
e.ProviderSpecific = ProviderSpecific{}
}
e.ProviderSpecific[key] = value
e.ProviderSpecific = append(e.ProviderSpecific, ProviderSpecificProperty{Name: key, Value: value})
return e
}
// GetProviderSpecificProperty returns a ProviderSpecificProperty if the property exists.
func (e *Endpoint) GetProviderSpecificProperty(key string) (ProviderSpecificProperty, bool) {
for _, providerSpecific := range e.ProviderSpecific {
if providerSpecific.Name == key {
return providerSpecific, true
}
}
return ProviderSpecificProperty{}, false
}
func (e *Endpoint) String() string {
return fmt.Sprintf("%s %d IN %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Targets, e.ProviderSpecific)
}
......
......@@ -17,6 +17,7 @@ limitations under the License.
package testutils
import (
"reflect"
"sort"
"github.com/kubernetes-incubator/external-dns/endpoint"
......@@ -49,7 +50,7 @@ func SameEndpoint(a, b *endpoint.Endpoint) bool {
return a.DNSName == b.DNSName && a.Targets.Same(b.Targets) && a.RecordType == b.RecordType &&
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL &&
a.Labels[endpoint.ResourceLabelKey] == b.Labels[endpoint.ResourceLabelKey] &&
SameMap(a.ProviderSpecific, b.ProviderSpecific)
SameProverSpecific(a.ProviderSpecific, b.ProviderSpecific)
}
// SameEndpoints compares two slices of endpoints regardless of order
......@@ -81,17 +82,7 @@ func SamePlanChanges(a, b map[string][]*endpoint.Endpoint) bool {
SameEndpoints(a["UpdateOld"], b["UpdateOld"]) && SameEndpoints(a["UpdateNew"], b["UpdateNew"])
}
// SameMap verifies that two maps contain the same string/string key/value pairs
func SameMap(a, b map[string]string) bool {
if len(a) != len(b) {
return false
}
for k, v := range a {
if v != b[k] {
return false
}
}
return true
// SameProverSpecific verifies that two maps contain the same string/string key/value pairs
func SameProverSpecific(a, b endpoint.ProviderSpecific) bool {
return reflect.DeepEqual(a, b)
}
......@@ -56,9 +56,11 @@ func ExampleSameEndpoints() {
RecordTTL: endpoint.TTL(60),
},
{
DNSName: "example.org",
Targets: endpoint.Targets{"load-balancer.org"},
ProviderSpecific: endpoint.ProviderSpecific{"foo": "bar"},
DNSName: "example.org",
Targets: endpoint.Targets{"load-balancer.org"},
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{Name: "foo", Value: "bar"},
},
},
}
sort.Sort(byAllFields(eps))
......@@ -66,11 +68,11 @@ func ExampleSameEndpoints() {
fmt.Println(ep)
}
// Output:
// abc.com 0 IN A 1.2.3.4 map[]
// abc.com 0 IN TXT something map[]
// bbc.com 0 IN CNAME foo.com map[]
// cbc.com 60 IN CNAME foo.com map[]
// example.org 0 IN load-balancer.org map[]
// example.org 0 IN load-balancer.org map[foo:bar]
// example.org 0 IN TXT load-balancer.org map[]
// abc.com 0 IN A 1.2.3.4 []
// abc.com 0 IN TXT something []
// bbc.com 0 IN CNAME foo.com []
// cbc.com 60 IN CNAME foo.com []
// example.org 0 IN load-balancer.org []
// example.org 0 IN load-balancer.org [{foo bar}]
// example.org 0 IN TXT load-balancer.org []
}
......@@ -116,6 +116,7 @@ func main() {
BatchChangeInterval: cfg.AWSBatchChangeInterval,
EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth,
AssumeRole: cfg.AWSAssumeRole,
APIRetries: cfg.AWSAPIRetries,
DryRun: cfg.DryRun,
},
)
......
......@@ -61,6 +61,7 @@ type Config struct {
AWSBatchChangeSize int
AWSBatchChangeInterval time.Duration
AWSEvaluateTargetHealth bool
AWSAPIRetries int
AzureConfigFile string
AzureResourceGroup string
CloudflareProxied bool
......@@ -134,6 +135,7 @@ var defaultConfig = &Config{
AWSBatchChangeSize: 1000,
AWSBatchChangeInterval: time.Second,
AWSEvaluateTargetHealth: true,
AWSAPIRetries: 3,
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
CloudflareProxied: false,
......@@ -250,6 +252,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("aws-batch-change-size", "When using the AWS provider, set the maximum number of changes that will be applied in each batch.").Default(strconv.Itoa(defaultConfig.AWSBatchChangeSize)).IntVar(&cfg.AWSBatchChangeSize)
app.Flag("aws-batch-change-interval", "When using the AWS provider, set the interval between batch changes.").Default(defaultConfig.AWSBatchChangeInterval.String()).DurationVar(&cfg.AWSBatchChangeInterval)
app.Flag("aws-evaluate-target-health", "When using the AWS provider, set whether to evaluate the health of a DNS target (default: enabled, disable with --no-aws-evaluate-target-health)").Default(strconv.FormatBool(defaultConfig.AWSEvaluateTargetHealth)).BoolVar(&cfg.AWSEvaluateTargetHealth)
app.Flag("aws-api-retries", "When using the AWS provider, set the maximum number of retries for API calls before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries)
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
......
......@@ -48,6 +48,7 @@ var (
AWSBatchChangeSize: 1000,
AWSBatchChangeInterval: time.Second,
AWSEvaluateTargetHealth: true,
AWSAPIRetries: 3,
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
CloudflareProxied: false,
......@@ -101,6 +102,7 @@ var (
AWSBatchChangeSize: 100,
AWSBatchChangeInterval: time.Second * 2,
AWSEvaluateTargetHealth: false,
AWSAPIRetries: 13,
AzureConfigFile: "azure.json",
AzureResourceGroup: "arg",
CloudflareProxied: true,
......@@ -198,6 +200,7 @@ func TestParseFlags(t *testing.T) {
"--aws-assume-role=some-other-role",
"--aws-batch-change-size=100",
"--aws-batch-change-interval=2s",
"--aws-api-retries=13",
"--no-aws-evaluate-target-health",
"--policy=upsert-only",
"--registry=noop",
......@@ -260,6 +263,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_AWS_BATCH_CHANGE_SIZE": "100",
"EXTERNAL_DNS_AWS_BATCH_CHANGE_INTERVAL": "2s",
"EXTERNAL_DNS_AWS_EVALUATE_TARGET_HEALTH": "0",
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
"EXTERNAL_DNS_POLICY": "upsert-only",
"EXTERNAL_DNS_REGISTRY": "noop",
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
......
......@@ -20,6 +20,7 @@ import (
"fmt"
"strings"
"github.com/google/go-cmp/cmp"
"github.com/kubernetes-incubator/external-dns/endpoint"
)
......@@ -84,7 +85,7 @@ func (t planTableRow) String() string {
}
func (t planTable) addCurrent(e *endpoint.Endpoint) {
dnsName := sanitizeDNSName(e.DNSName)
dnsName := normalizeDNSName(e.DNSName)
if _, ok := t.rows[dnsName]; !ok {
t.rows[dnsName] = &planTableRow{}
}
......@@ -92,7 +93,7 @@ func (t planTable) addCurrent(e *endpoint.Endpoint) {
}
func (t planTable) addCandidate(e *endpoint.Endpoint) {
dnsName := sanitizeDNSName(e.DNSName)
dnsName := normalizeDNSName(e.DNSName)
if _, ok := t.rows[dnsName]; !ok {
t.rows[dnsName] = &planTableRow{}
}
......@@ -105,7 +106,7 @@ func (t planTable) getUpdates() (updateNew []*endpoint.Endpoint, updateOld []*en
if row.current != nil && len(row.candidates) > 0 { //dns name is taken
update := t.resolver.ResolveUpdate(row.current, row.candidates)
// compare "update" to "current" to figure out if actual update is required
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) {
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || shouldUpdateProviderSpecific(update, row.current) {
inheritOwner(row.current, update)
updateNew = append(updateNew, update)
updateOld = append(updateOld, row.current)
......@@ -185,6 +186,10 @@ func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool {
return desired.RecordTTL != current.RecordTTL
}
func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool {
return !cmp.Equal(desired.ProviderSpecific, current.ProviderSpecific)
}
// filterRecordsForPlan removes records that are not relevant to the planner.
// Currently this just removes TXT records to prevent them from being
// deleted erroneously by the planner (only the TXT registry should do this.)
......@@ -209,8 +214,12 @@ func filterRecordsForPlan(records []*endpoint.Endpoint) []*endpoint.Endpoint {
return filtered
}
// sanitizeDNSName checks if the DNS name is correct
// for now it only removes space and lower case
func sanitizeDNSName(dnsName string) string {
return strings.TrimSpace(strings.ToLower(dnsName))
// normalizeDNSName converts a DNS name to a canonical form, so that we can use string equality
// it: removes space, converts to lower case, ensures there is a trailing dot
func normalizeDNSName(dnsName string) string {
s := strings.TrimSpace(strings.ToLower(dnsName))
if !strings.HasSuffix(s, ".") {
s += "."
}
return s
}
......@@ -27,15 +27,17 @@ import (
type PlanTestSuite struct {
suite.Suite
fooV1Cname *endpoint.Endpoint
fooV2Cname *endpoint.Endpoint
fooV2TXT *endpoint.Endpoint
fooV2CnameNoLabel *endpoint.Endpoint
fooV3CnameSameResource *endpoint.Endpoint
fooA5 *endpoint.Endpoint
bar127A *endpoint.Endpoint
bar127AWithTTL *endpoint.Endpoint
bar192A *endpoint.Endpoint
fooV1Cname *endpoint.Endpoint
fooV2Cname *endpoint.Endpoint
fooV2TXT *endpoint.Endpoint
fooV2CnameNoLabel *endpoint.Endpoint
fooV3CnameSameResource *endpoint.Endpoint
fooA5 *endpoint.Endpoint
bar127A *endpoint.Endpoint
bar127AWithTTL *endpoint.Endpoint
bar127AWithProviderSpecificTrue *endpoint.Endpoint
bar127AWithProviderSpecificFalse *endpoint.Endpoint
bar192A *endpoint.Endpoint
}
func (suite *PlanTestSuite) SetupTest() {
......@@ -100,6 +102,34 @@ func (suite *PlanTestSuite) SetupTest() {
endpoint.ResourceLabelKey: "ingress/default/bar-127",
},
}
suite.bar127AWithProviderSpecificTrue = &endpoint.Endpoint{
DNSName: "bar",
Targets: endpoint.Targets{"127.0.0.1"},
RecordType: "A",
Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-127",
},
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
Value: "true",
},
},
}
suite.bar127AWithProviderSpecificFalse = &endpoint.Endpoint{
DNSName: "bar",
Targets: endpoint.Targets{"127.0.0.1"},
RecordType: "A",
Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-127",
},
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
Value: "false",
},
},
}
suite.bar192A = &endpoint.Endpoint{
DNSName: "bar",
Targets: endpoint.Targets{"192.168.0.1"},
......@@ -108,6 +138,7 @@ func (suite *PlanTestSuite) SetupTest() {
endpoint.ResourceLabelKey: "ingress/default/bar-192",
},
}
}
func (suite *PlanTestSuite) TestSyncFirstRound() {
......@@ -194,6 +225,27 @@ func (suite *PlanTestSuite) TestSyncSecondRoundWithTTLChange() {
validateEntries(suite.T(), changes.Delete, expectedDelete)
}
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificChange() {
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
expectedCreate := []*endpoint.Endpoint{}
expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
expectedDelete := []*endpoint.Endpoint{}
p := &Plan{
Policies: []Policy{&SyncPolicy{}},
Current: current,
Desired: desired,
}
changes := p.Calculate().Changes
validateEntries(suite.T(), changes.Create, expectedCreate)
validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew)
validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld)
validateEntries(suite.T(), changes.Delete, expectedDelete)
}
func (suite *PlanTestSuite) TestSyncSecondRoundWithOwnerInherited() {
current := []*endpoint.Endpoint{suite.fooV1Cname}
desired := []*endpoint.Endpoint{suite.fooV2Cname}
......@@ -354,6 +406,7 @@ func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() {
//TODO: remove once multiple-target per endpoint is supported
func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() {
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A}
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource}
expectedCreate := []*endpoint.Endpoint{}
......@@ -385,54 +438,58 @@ func validateEntries(t *testing.T, entries, expected []*endpoint.Endpoint) {
}
}
func TestSanitizeDNSName(t *testing.T) {
func TestNormalizeDNSName(t *testing.T) {
records := []struct {
dnsName string
expect string
}{
{
"3AAAA.FOO.BAR.COM ",
"3aaaa.foo.bar.com",
"3aaaa.foo.bar.com.",
},
{
" example.foo.com",
"example.foo.com",
" example.foo.com.",
"example.foo.com.",
},
{
"example123.foo.com ",
"example123.foo.com",
"example123.foo.com.",
},
{
"foo",
"foo",
"foo.",
},
{
"123foo.bar",
"123foo.bar",
"123foo.bar.",
},
{
"foo.com",
"foo.com",
"foo.com.",
},
{
"foo.com.",
"foo.com.",
},
{
"foo123.COM",
"foo123.com",
"foo123.com.",
},
{
"my-exaMple3.FOO.BAR.COM",
"my-example3.foo.bar.com",
"my-example3.foo.bar.com.",
},
{
" my-example1214.FOO-1235.BAR-foo.COM ",
"my-example1214.foo-1235.bar-foo.com",
"my-example1214.foo-1235.bar-foo.com.",
},
{
"my-example-my-example-1214.FOO-1235.BAR-foo.COM",
"my-example-my-example-1214.foo-1235.bar-foo.com",
"my-example-my-example-1214.foo-1235.bar-foo.com.",
},
}
for _, r := range records {
gotName := sanitizeDNSName(r.dnsName)
gotName := normalizeDNSName(r.dnsName)
assert.Equal(t, r.expect, gotName)
}
}
......@@ -117,12 +117,13 @@ type AWSConfig struct {
BatchChangeInterval time.Duration
EvaluateTargetHealth bool
AssumeRole string
APIRetries int
DryRun bool
}
// NewAWSProvider initializes a new AWS Route53 based Provider.
func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
config := aws.NewConfig()
config := aws.NewConfig().WithMaxRetries(awsConfig.APIRetries)
config.WithHTTPClient(
instrumented_http.NewClient(config.HTTPClient, &instrumented_http.Callbacks{
......@@ -394,8 +395,8 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou
if isAWSLoadBalancer(endpoint) {
evalTargetHealth := p.evaluateTargetHealth
if _, ok := endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth]; ok {
evalTargetHealth = endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth] == "true"
if prop, ok := endpoint.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok {
evalTargetHealth = prop.Value == "true"
}
change.ResourceRecordSet.Type = aws.String(route53.RRTypeA)
......@@ -587,7 +588,7 @@ func isAWSLoadBalancer(ep *endpoint.Endpoint) bool {
// isAWSAlias determines if a given hostname belongs to an AWS Alias record by doing an reverse lookup.
func isAWSAlias(ep *endpoint.Endpoint, addrs []*endpoint.Endpoint) string {
if val, exists := ep.ProviderSpecific["alias"]; ep.RecordType == endpoint.RecordTypeCNAME && exists && val == "true" {
if prop, exists := ep.GetProviderSpecificProperty("alias"); ep.RecordType == endpoint.RecordTypeCNAME && exists && prop.Value == "true" {
for _, addr := range addrs {
if addr.DNSName == ep.Targets[0] {
if hostedZone := canonicalHostedZone(addr.Targets[0]); hostedZone != "" {
......
......@@ -799,7 +799,10 @@ func TestAWSCreateRecordsWithALIAS(t *testing.T) {
Targets: endpoint.Targets{"foo.eu-central-1.elb.amazonaws.com"},
RecordType: endpoint.RecordTypeCNAME,
ProviderSpecific: endpoint.ProviderSpecific{
providerSpecificEvaluateTargetHealth: key,
endpoint.ProviderSpecificProperty{
Name: providerSpecificEvaluateTargetHealth,
Value: key,
},
},
},
}
......@@ -850,9 +853,14 @@ func TestAWSisAWSAlias(t *testing.T) {
{"foo.example.org", endpoint.RecordTypeCNAME, "true", ""},
} {
ep := &endpoint.Endpoint{
Targets: endpoint.Targets{tc.target},
RecordType: tc.recordType,
ProviderSpecific: map[string]string{"alias": tc.alias},
Targets: endpoint.Targets{tc.target},
RecordType: tc.recordType,
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "alias",
Value: tc.alias,
},
},
}
addrs := []*endpoint.Endpoint{
{
......
......@@ -410,6 +410,36 @@ func TestNewCloudFlareChangeNoProxied(t *testing.T) {
assert.False(t, change.ResourceRecordSet.Proxied)
}
func TestNewCloudFlareProxiedAnnotationTrue(t *testing.T) {
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
Value: "true",
},
}}, false)
assert.True(t, change.ResourceRecordSet.Proxied)
}
func TestNewCloudFlareProxiedAnnotationFalse(t *testing.T) {
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
Value: "false",
},
}}, true)
assert.False(t, change.ResourceRecordSet.Proxied)
}
func TestNewCloudFlareProxiedAnnotationIllegalValue(t *testing.T) {
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
Value: "asdaslkjndaslkdjals",
},
}}, false)
assert.False(t, change.ResourceRecordSet.Proxied)
}
func TestNewCloudFlareChangeProxiable(t *testing.T) {
var cloudFlareTypes = []struct {
recordType string
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment