mirror of
				https://github.com/kubernetes-sigs/external-dns.git
				synced 2025-10-31 10:41:16 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			708 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			708 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2017 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package provider
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	dns "google.golang.org/api/dns/v1"
 | |
| 
 | |
| 	"golang.org/x/net/context"
 | |
| 
 | |
| 	"github.com/kubernetes-sigs/external-dns/endpoint"
 | |
| 	"github.com/kubernetes-sigs/external-dns/plan"
 | |
| 
 | |
| 	"google.golang.org/api/googleapi"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	testZones   = map[string]*dns.ManagedZone{}
 | |
| 	testRecords = map[string]map[string]*dns.ResourceRecordSet{}
 | |
| )
 | |
| 
 | |
| type mockManagedZonesCreateCall struct {
 | |
| 	project     string
 | |
| 	managedZone *dns.ManagedZone
 | |
| }
 | |
| 
 | |
| func (m *mockManagedZonesCreateCall) Do(opts ...googleapi.CallOption) (*dns.ManagedZone, error) {
 | |
| 	zoneKey := zoneKey(m.project, m.managedZone.Name)
 | |
| 
 | |
| 	if _, ok := testZones[zoneKey]; ok {
 | |
| 		return nil, &googleapi.Error{Code: http.StatusConflict}
 | |
| 	}
 | |
| 
 | |
| 	testZones[zoneKey] = m.managedZone
 | |
| 
 | |
| 	return m.managedZone, nil
 | |
| }
 | |
| 
 | |
| type mockManagedZonesListCall struct {
 | |
| 	project string
 | |
| }
 | |
| 
 | |
| func (m *mockManagedZonesListCall) Pages(ctx context.Context, f func(*dns.ManagedZonesListResponse) error) error {
 | |
| 	zones := []*dns.ManagedZone{}
 | |
| 
 | |
| 	for k, v := range testZones {
 | |
| 		if strings.HasPrefix(k, m.project+"/") {
 | |
| 			zones = append(zones, v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return f(&dns.ManagedZonesListResponse{ManagedZones: zones})
 | |
| }
 | |
| 
 | |
| type mockManagedZonesClient struct{}
 | |
| 
 | |
| func (m *mockManagedZonesClient) Create(project string, managedZone *dns.ManagedZone) managedZonesCreateCallInterface {
 | |
| 	return &mockManagedZonesCreateCall{project: project, managedZone: managedZone}
 | |
| }
 | |
| 
 | |
| func (m *mockManagedZonesClient) List(project string) managedZonesListCallInterface {
 | |
| 	return &mockManagedZonesListCall{project: project}
 | |
| }
 | |
| 
 | |
| type mockResourceRecordSetsListCall struct {
 | |
| 	project     string
 | |
| 	managedZone string
 | |
| }
 | |
| 
 | |
| func (m *mockResourceRecordSetsListCall) Pages(ctx context.Context, f func(*dns.ResourceRecordSetsListResponse) error) error {
 | |
| 	zoneKey := zoneKey(m.project, m.managedZone)
 | |
| 
 | |
| 	if _, ok := testZones[zoneKey]; !ok {
 | |
| 		return &googleapi.Error{Code: http.StatusNotFound}
 | |
| 	}
 | |
| 
 | |
| 	resp := []*dns.ResourceRecordSet{}
 | |
| 
 | |
| 	for _, v := range testRecords[zoneKey] {
 | |
| 		resp = append(resp, v)
 | |
| 	}
 | |
| 
 | |
| 	return f(&dns.ResourceRecordSetsListResponse{Rrsets: resp})
 | |
| }
 | |
| 
 | |
| type mockResourceRecordSetsClient struct{}
 | |
| 
 | |
| func (m *mockResourceRecordSetsClient) List(project string, managedZone string) resourceRecordSetsListCallInterface {
 | |
| 	return &mockResourceRecordSetsListCall{project: project, managedZone: managedZone}
 | |
| }
 | |
| 
 | |
| type mockChangesCreateCall struct {
 | |
| 	project     string
 | |
| 	managedZone string
 | |
| 	change      *dns.Change
 | |
| }
 | |
| 
 | |
| func (m *mockChangesCreateCall) Do(opts ...googleapi.CallOption) (*dns.Change, error) {
 | |
| 	zoneKey := zoneKey(m.project, m.managedZone)
 | |
| 
 | |
| 	if _, ok := testZones[zoneKey]; !ok {
 | |
| 		return nil, &googleapi.Error{Code: http.StatusNotFound}
 | |
| 	}
 | |
| 
 | |
| 	if _, ok := testRecords[zoneKey]; !ok {
 | |
| 		testRecords[zoneKey] = make(map[string]*dns.ResourceRecordSet)
 | |
| 	}
 | |
| 
 | |
| 	for _, c := range append(m.change.Additions, m.change.Deletions...) {
 | |
| 		if !isValidRecordSet(c) {
 | |
| 			return nil, &googleapi.Error{
 | |
| 				Code:    http.StatusBadRequest,
 | |
| 				Message: fmt.Sprintf("invalid record: %v", c),
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, del := range m.change.Deletions {
 | |
| 		recordKey := recordKey(del.Type, del.Name)
 | |
| 		delete(testRecords[zoneKey], recordKey)
 | |
| 	}
 | |
| 
 | |
| 	for _, add := range m.change.Additions {
 | |
| 		recordKey := recordKey(add.Type, add.Name)
 | |
| 		testRecords[zoneKey][recordKey] = add
 | |
| 	}
 | |
| 
 | |
| 	return m.change, nil
 | |
| }
 | |
| 
 | |
| type mockChangesClient struct{}
 | |
| 
 | |
| func (m *mockChangesClient) Create(project string, managedZone string, change *dns.Change) changesCreateCallInterface {
 | |
| 	return &mockChangesCreateCall{project: project, managedZone: managedZone, change: change}
 | |
| }
 | |
| 
 | |
| func zoneKey(project, zoneName string) string {
 | |
| 	return project + "/" + zoneName
 | |
| }
 | |
| 
 | |
| func recordKey(recordType, recordName string) string {
 | |
| 	return recordType + "/" + recordName
 | |
| }
 | |
| 
 | |
| func isValidRecordSet(recordSet *dns.ResourceRecordSet) bool {
 | |
| 	if !hasTrailingDot(recordSet.Name) {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	switch recordSet.Type {
 | |
| 	case endpoint.RecordTypeCNAME:
 | |
| 		for _, rrd := range recordSet.Rrdatas {
 | |
| 			if !hasTrailingDot(rrd) {
 | |
| 				return false
 | |
| 			}
 | |
| 		}
 | |
| 	case endpoint.RecordTypeA, endpoint.RecordTypeTXT:
 | |
| 		for _, rrd := range recordSet.Rrdatas {
 | |
| 			if hasTrailingDot(rrd) {
 | |
| 				return false
 | |
| 			}
 | |
| 		}
 | |
| 	default:
 | |
| 		panic("unhandled record type")
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func hasTrailingDot(target string) bool {
 | |
| 	return strings.HasSuffix(target, ".")
 | |
| }
 | |
| 
 | |
| func TestGoogleZonesIDFilter(t *testing.T) {
 | |
| 	provider := newGoogleProviderZoneOverlap(t, NewDomainFilter([]string{"cluster.local."}), NewZoneIDFilter([]string{"10002"}), false, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	zones, err := provider.Zones()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateZones(t, zones, map[string]*dns.ManagedZone{
 | |
| 		"internal-2": {Name: "internal-2", DnsName: "cluster.local.", Id: 10002},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleZonesNameFilter(t *testing.T) {
 | |
| 	provider := newGoogleProviderZoneOverlap(t, NewDomainFilter([]string{"cluster.local."}), NewZoneIDFilter([]string{"internal-2"}), false, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	zones, err := provider.Zones()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateZones(t, zones, map[string]*dns.ManagedZone{
 | |
| 		"internal-2": {Name: "internal-2", DnsName: "cluster.local.", Id: 10002},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleZones(t *testing.T) {
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	zones, err := provider.Zones()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateZones(t, zones, map[string]*dns.ManagedZone{
 | |
| 		"zone-1-ext-dns-test-2-gcp-zalan-do": {Name: "zone-1-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-1.ext-dns-test-2.gcp.zalan.do."},
 | |
| 		"zone-2-ext-dns-test-2-gcp-zalan-do": {Name: "zone-2-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-2.ext-dns-test-2.gcp.zalan.do."},
 | |
| 		"zone-3-ext-dns-test-2-gcp-zalan-do": {Name: "zone-3-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-3.ext-dns-test-2.gcp.zalan.do."},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleRecords(t *testing.T) {
 | |
| 	originalEndpoints := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(1), "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(2), "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(3), "foo.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, originalEndpoints)
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, originalEndpoints)
 | |
| }
 | |
| 
 | |
| func TestGoogleRecordsFilter(t *testing.T) {
 | |
| 	originalEndpoints := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	provider := newGoogleProvider(
 | |
| 		t,
 | |
| 		NewDomainFilter([]string{
 | |
| 			// our two valid zones
 | |
| 			"zone-1.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			"zone-2.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			// we filter for a zone that doesn't exist, should have no effect.
 | |
| 			"zone-0.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			// there exists a third zone "zone-3" that we want to exclude from being managed.
 | |
| 		}),
 | |
| 		NewZoneIDFilter([]string{""}),
 | |
| 		false,
 | |
| 		originalEndpoints,
 | |
| 	)
 | |
| 
 | |
| 	// these records should be filtered out since they don't match a hosted zone or domain filter.
 | |
| 	ignoredEndpoints := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("filter-create-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("filter-update-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("filter-delete-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("filter-create-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("filter-update-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("filter-delete-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, provider.CreateRecords(ignoredEndpoints))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// assert that due to filtering no changes were made.
 | |
| 	validateEndpoints(t, records, originalEndpoints)
 | |
| }
 | |
| 
 | |
| func TestGoogleCreateRecords(t *testing.T) {
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	records := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, provider.CreateRecords(records))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "foo.elb.amazonaws.com"),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleUpdateRecords(t *testing.T) {
 | |
| 	currentRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "foo.elb.amazonaws.com"),
 | |
| 	}
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, currentRecords)
 | |
| 	updatedRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"),
 | |
| 		endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, provider.UpdateRecords(updatedRecords, currentRecords))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleDeleteRecords(t *testing.T) {
 | |
| 	originalEndpoints := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "baz.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, originalEndpoints)
 | |
| 
 | |
| 	require.NoError(t, provider.DeleteRecords(originalEndpoints))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, []*endpoint.Endpoint{})
 | |
| }
 | |
| 
 | |
| func TestGoogleApplyChanges(t *testing.T) {
 | |
| 	provider := newGoogleProvider(
 | |
| 		t,
 | |
| 		NewDomainFilter([]string{
 | |
| 			// our two valid zones
 | |
| 			"zone-1.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			"zone-2.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			// we filter for a zone that doesn't exist, should have no effect.
 | |
| 			"zone-0.ext-dns-test-2.gcp.zalan.do.",
 | |
| 			// there exists a third zone "zone-3" that we want to exclude from being managed.
 | |
| 		}),
 | |
| 		NewZoneIDFilter([]string{""}),
 | |
| 		false,
 | |
| 		[]*endpoint.Endpoint{
 | |
| 			endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 			endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 			endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(10), "8.8.4.4"),
 | |
| 			endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"),
 | |
| 			endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"),
 | |
| 			endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
 | |
| 		},
 | |
| 	)
 | |
| 
 | |
| 	createRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpoint("filter-create-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("nomatch-create-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.1"),
 | |
| 	}
 | |
| 
 | |
| 	currentRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpoint("filter-update-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 	}
 | |
| 	updatedRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"),
 | |
| 		endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "baz.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpoint("filter-update-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "5.6.7.8"),
 | |
| 		endpoint.NewEndpoint("nomatch-update-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.7.6.5"),
 | |
| 	}
 | |
| 
 | |
| 	deleteRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpoint("filter-delete-test.zone-3.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.2"),
 | |
| 		endpoint.NewEndpoint("nomatch-delete-test.zone-0.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.2.2.1"),
 | |
| 	}
 | |
| 
 | |
| 	changes := &plan.Changes{
 | |
| 		Create:    createRecords,
 | |
| 		UpdateNew: updatedRecords,
 | |
| 		UpdateOld: currentRecords,
 | |
| 		Delete:    deleteRecords,
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, provider.ApplyChanges(context.Background(), changes))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"),
 | |
| 		endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "foo.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "baz.elb.amazonaws.com"),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestGoogleApplyChangesDryRun(t *testing.T) {
 | |
| 	originalEndpoints := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), true, originalEndpoints)
 | |
| 
 | |
| 	createRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	currentRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"),
 | |
| 	}
 | |
| 	updatedRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "1.2.3.4"),
 | |
| 		endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "4.3.2.1"),
 | |
| 		endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "baz.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	deleteRecords := []*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.4.4"),
 | |
| 		endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"),
 | |
| 	}
 | |
| 
 | |
| 	changes := &plan.Changes{
 | |
| 		Create:    createRecords,
 | |
| 		UpdateNew: updatedRecords,
 | |
| 		UpdateOld: currentRecords,
 | |
| 		Delete:    deleteRecords,
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, provider.ApplyChanges(context.Background(), changes))
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, originalEndpoints)
 | |
| }
 | |
| 
 | |
| func TestGoogleApplyChangesEmpty(t *testing.T) {
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
 | |
| 	assert.NoError(t, provider.ApplyChanges(context.Background(), &plan.Changes{}))
 | |
| }
 | |
| 
 | |
| func TestNewFilteredRecords(t *testing.T) {
 | |
| 	provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	records := provider.newFilteredRecords([]*endpoint.Endpoint{
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 1, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 120, "8.8.4.4"),
 | |
| 		endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, 4000, "bar.elb.amazonaws.com"),
 | |
| 		// test fallback to Ttl:300 when Ttl==0 :
 | |
| 		endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 0, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
 | |
| 		endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"),
 | |
| 	})
 | |
| 
 | |
| 	validateChangeRecords(t, records, []*dns.ResourceRecordSet{
 | |
| 		{Name: "update-test.zone-2.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.4.4"}, Type: "A", Ttl: 1},
 | |
| 		{Name: "delete-test.zone-2.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.4.4"}, Type: "A", Ttl: 120},
 | |
| 		{Name: "update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"bar.elb.amazonaws.com."}, Type: "CNAME", Ttl: 4000},
 | |
| 		{Name: "update-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300},
 | |
| 		{Name: "delete-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300},
 | |
| 		{Name: "delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"qux.elb.amazonaws.com."}, Type: "CNAME", Ttl: 300},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestSeparateChanges(t *testing.T) {
 | |
| 	change := &dns.Change{
 | |
| 		Additions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "qux.foo.example.org.", Ttl: 1},
 | |
| 			{Name: "qux.bar.example.org.", Ttl: 2},
 | |
| 		},
 | |
| 		Deletions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "wambo.foo.example.org.", Ttl: 10},
 | |
| 			{Name: "wambo.bar.example.org.", Ttl: 20},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	zones := map[string]*dns.ManagedZone{
 | |
| 		"foo-example-org": {
 | |
| 			Name:    "foo-example-org",
 | |
| 			DnsName: "foo.example.org.",
 | |
| 		},
 | |
| 		"bar-example-org": {
 | |
| 			Name:    "bar-example-org",
 | |
| 			DnsName: "bar.example.org.",
 | |
| 		},
 | |
| 		"baz-example-org": {
 | |
| 			Name:    "baz-example-org",
 | |
| 			DnsName: "baz.example.org.",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	changes := separateChange(zones, change)
 | |
| 	require.Len(t, changes, 2)
 | |
| 
 | |
| 	validateChange(t, changes["foo-example-org"], &dns.Change{
 | |
| 		Additions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "qux.foo.example.org.", Ttl: 1},
 | |
| 		},
 | |
| 		Deletions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "wambo.foo.example.org.", Ttl: 10},
 | |
| 		},
 | |
| 	})
 | |
| 
 | |
| 	validateChange(t, changes["bar-example-org"], &dns.Change{
 | |
| 		Additions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "qux.bar.example.org.", Ttl: 2},
 | |
| 		},
 | |
| 		Deletions: []*dns.ResourceRecordSet{
 | |
| 			{Name: "wambo.bar.example.org.", Ttl: 20},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func validateZones(t *testing.T, zones map[string]*dns.ManagedZone, expected map[string]*dns.ManagedZone) {
 | |
| 	require.Len(t, zones, len(expected))
 | |
| 
 | |
| 	for i, zone := range zones {
 | |
| 		validateZone(t, zone, expected[i])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func validateZone(t *testing.T, zone *dns.ManagedZone, expected *dns.ManagedZone) {
 | |
| 	assert.Equal(t, expected.Name, zone.Name)
 | |
| 	assert.Equal(t, expected.DnsName, zone.DnsName)
 | |
| }
 | |
| 
 | |
| func validateChange(t *testing.T, change *dns.Change, expected *dns.Change) {
 | |
| 	validateChangeRecords(t, change.Additions, expected.Additions)
 | |
| 	validateChangeRecords(t, change.Deletions, expected.Deletions)
 | |
| }
 | |
| 
 | |
| func validateChangeRecords(t *testing.T, records []*dns.ResourceRecordSet, expected []*dns.ResourceRecordSet) {
 | |
| 	require.Len(t, records, len(expected))
 | |
| 
 | |
| 	for i := range records {
 | |
| 		validateChangeRecord(t, records[i], expected[i])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected *dns.ResourceRecordSet) {
 | |
| 	assert.Equal(t, expected.Name, record.Name)
 | |
| 	assert.Equal(t, expected.Rrdatas, record.Rrdatas)
 | |
| 	assert.Equal(t, expected.Ttl, record.Ttl)
 | |
| 	assert.Equal(t, expected.Type, record.Type)
 | |
| }
 | |
| 
 | |
| func newGoogleProviderZoneOverlap(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider {
 | |
| 	provider := &GoogleProvider{
 | |
| 		project:                  "zalando-external-dns-test",
 | |
| 		dryRun:                   false,
 | |
| 		domainFilter:             domainFilter,
 | |
| 		zoneIDFilter:             zoneIDFilter,
 | |
| 		resourceRecordSetsClient: &mockResourceRecordSetsClient{},
 | |
| 		managedZonesClient:       &mockManagedZonesClient{},
 | |
| 		changesClient:            &mockChangesClient{},
 | |
| 	}
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "internal-1",
 | |
| 		DnsName: "cluster.local.",
 | |
| 		Id:      10001,
 | |
| 	})
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "internal-2",
 | |
| 		DnsName: "cluster.local.",
 | |
| 		Id:      10002,
 | |
| 	})
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "internal-3",
 | |
| 		DnsName: "cluster.local.",
 | |
| 		Id:      10003,
 | |
| 	})
 | |
| 
 | |
| 	provider.dryRun = dryRun
 | |
| 
 | |
| 	return provider
 | |
| 
 | |
| }
 | |
| 
 | |
| func newGoogleProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider {
 | |
| 	provider := &GoogleProvider{
 | |
| 		project:                  "zalando-external-dns-test",
 | |
| 		dryRun:                   false,
 | |
| 		domainFilter:             domainFilter,
 | |
| 		zoneIDFilter:             zoneIDFilter,
 | |
| 		resourceRecordSetsClient: &mockResourceRecordSetsClient{},
 | |
| 		managedZonesClient:       &mockManagedZonesClient{},
 | |
| 		changesClient:            &mockChangesClient{},
 | |
| 	}
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "zone-1-ext-dns-test-2-gcp-zalan-do",
 | |
| 		DnsName: "zone-1.ext-dns-test-2.gcp.zalan.do.",
 | |
| 	})
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "zone-2-ext-dns-test-2-gcp-zalan-do",
 | |
| 		DnsName: "zone-2.ext-dns-test-2.gcp.zalan.do.",
 | |
| 	})
 | |
| 
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "zone-3-ext-dns-test-2-gcp-zalan-do",
 | |
| 		DnsName: "zone-3.ext-dns-test-2.gcp.zalan.do.",
 | |
| 	})
 | |
| 
 | |
| 	// filtered out by domain filter
 | |
| 	createZone(t, provider, &dns.ManagedZone{
 | |
| 		Name:    "zone-4-ext-dns-test-3-gcp-zalan-do",
 | |
| 		DnsName: "zone-4.ext-dns-test-3.gcp.zalan.do.",
 | |
| 	})
 | |
| 
 | |
| 	setupGoogleRecords(t, provider, records)
 | |
| 
 | |
| 	provider.dryRun = dryRun
 | |
| 
 | |
| 	return provider
 | |
| }
 | |
| 
 | |
| func createZone(t *testing.T, provider *GoogleProvider, zone *dns.ManagedZone) {
 | |
| 	zone.Description = "Testing zone for kubernetes.io/external-dns"
 | |
| 
 | |
| 	if _, err := provider.managedZonesClient.Create("zalando-external-dns-test", zone).Do(); err != nil {
 | |
| 		if err, ok := err.(*googleapi.Error); !ok || err.Code != http.StatusConflict {
 | |
| 			require.NoError(t, err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func setupGoogleRecords(t *testing.T, provider *GoogleProvider, endpoints []*endpoint.Endpoint) {
 | |
| 	clearGoogleRecords(t, provider, "zone-1-ext-dns-test-2-gcp-zalan-do")
 | |
| 	clearGoogleRecords(t, provider, "zone-2-ext-dns-test-2-gcp-zalan-do")
 | |
| 	clearGoogleRecords(t, provider, "zone-3-ext-dns-test-2-gcp-zalan-do")
 | |
| 
 | |
| 	records, err := provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, []*endpoint.Endpoint{})
 | |
| 
 | |
| 	require.NoError(t, provider.CreateRecords(endpoints))
 | |
| 
 | |
| 	records, err = provider.Records()
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	validateEndpoints(t, records, endpoints)
 | |
| }
 | |
| 
 | |
| func clearGoogleRecords(t *testing.T, provider *GoogleProvider, zone string) {
 | |
| 	recordSets := []*dns.ResourceRecordSet{}
 | |
| 	require.NoError(t, provider.resourceRecordSetsClient.List(provider.project, zone).Pages(context.TODO(), func(resp *dns.ResourceRecordSetsListResponse) error {
 | |
| 		for _, r := range resp.Rrsets {
 | |
| 			switch r.Type {
 | |
| 			case endpoint.RecordTypeA, endpoint.RecordTypeCNAME:
 | |
| 				recordSets = append(recordSets, r)
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}))
 | |
| 
 | |
| 	if len(recordSets) != 0 {
 | |
| 		_, err := provider.changesClient.Create(provider.project, zone, &dns.Change{
 | |
| 			Deletions: recordSets,
 | |
| 		}).Do()
 | |
| 		require.NoError(t, err)
 | |
| 	}
 | |
| }
 |