New filter: --zone-id-filter (#422)

* Add aws-zone-id flag

* Add Zone ID filter

* Update AWS provider and main

* Make ZoneIDFilter generic

* Implement ZoneIDFilter for all providers

* Update CHANGELOG
This commit is contained in:
Valentyn Boginskey 2017-12-20 11:00:13 -05:00 committed by Martin Linkhorst
parent ec07f45c8e
commit 4dacf81238
18 changed files with 255 additions and 57 deletions

View File

@ -3,6 +3,7 @@
- Target of DNS record is changed only if corresponding kubernetes resource target changes - Target of DNS record is changed only if corresponding kubernetes resource target changes
- If kubernetes resource is deleted, then another resource may acquire DNS name - If kubernetes resource is deleted, then another resource may acquire DNS name
- "Flapping" target issue is resolved by providing a consistent and defined mechanism for choosing a target - "Flapping" target issue is resolved by providing a consistent and defined mechanism for choosing a target
- New `--zone-id-filter` parameter allows filtering by zone id
## v0.4.8 - 2017-11-22 ## v0.4.8 - 2017-11-22

12
main.go
View File

@ -88,26 +88,28 @@ func main() {
endpointsSource := source.NewDedupSource(source.NewMultiSource(sources)) endpointsSource := source.NewDedupSource(source.NewMultiSource(sources))
domainFilter := provider.NewDomainFilter(cfg.DomainFilter) domainFilter := provider.NewDomainFilter(cfg.DomainFilter)
zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType) zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
var p provider.Provider var p provider.Provider
switch cfg.Provider { switch cfg.Provider {
case "aws": case "aws":
p, err = provider.NewAWSProvider(domainFilter, zoneTypeFilter, cfg.DryRun) p, err = provider.NewAWSProvider(domainFilter, zoneIDFilter, zoneTypeFilter, cfg.DryRun)
case "azure": case "azure":
p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, cfg.AzureResourceGroup, cfg.DryRun) p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
case "cloudflare": case "cloudflare":
p, err = provider.NewCloudFlareProvider(domainFilter, cfg.CloudflareProxied, cfg.DryRun) p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun)
case "google": case "google":
p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, cfg.DryRun) p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
case "digitalocean": case "digitalocean":
p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun) p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
case "dnsimple": case "dnsimple":
p, err = provider.NewDnsimpleProvider(domainFilter, cfg.DryRun) p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
case "infoblox": case "infoblox":
p, err = provider.NewInfobloxProvider( p, err = provider.NewInfobloxProvider(
provider.InfobloxConfig{ provider.InfobloxConfig{
DomainFilter: domainFilter, DomainFilter: domainFilter,
ZoneIDFilter: zoneIDFilter,
Host: cfg.InfobloxGridHost, Host: cfg.InfobloxGridHost,
Port: cfg.InfobloxWapiPort, Port: cfg.InfobloxWapiPort,
Username: cfg.InfobloxWapiUsername, Username: cfg.InfobloxWapiUsername,

View File

@ -41,6 +41,7 @@ type Config struct {
Provider string Provider string
GoogleProject string GoogleProject string
DomainFilter []string DomainFilter []string
ZoneIDFilter []string
AWSZoneType string AWSZoneType string
AzureConfigFile string AzureConfigFile string
AzureResourceGroup string AzureResourceGroup string
@ -134,6 +135,7 @@ func (cfg *Config) ParseFlags(args []string) error {
// Flags related to providers // Flags related to providers
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory") app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
app.Flag("google-project", "When using the Google provider, specify the Google project (required when --provider=google)").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject) app.Flag("google-project", "When using the Google provider, specify the Google project (required when --provider=google)").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
app.Flag("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private") app.Flag("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private")
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-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)

View File

@ -37,6 +37,7 @@ var (
Provider: "google", Provider: "google",
GoogleProject: "", GoogleProject: "",
DomainFilter: []string{""}, DomainFilter: []string{""},
ZoneIDFilter: []string{""},
AWSZoneType: "", AWSZoneType: "",
AzureConfigFile: "/etc/kubernetes/azure.json", AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "", AzureResourceGroup: "",
@ -70,6 +71,7 @@ var (
Provider: "google", Provider: "google",
GoogleProject: "project", GoogleProject: "project",
DomainFilter: []string{"example.org", "company.com"}, DomainFilter: []string{"example.org", "company.com"},
ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
AWSZoneType: "private", AWSZoneType: "private",
AzureConfigFile: "azure.json", AzureConfigFile: "azure.json",
AzureResourceGroup: "arg", AzureResourceGroup: "arg",
@ -135,6 +137,8 @@ func TestParseFlags(t *testing.T) {
"--no-infoblox-ssl-verify", "--no-infoblox-ssl-verify",
"--domain-filter=example.org", "--domain-filter=example.org",
"--domain-filter=company.com", "--domain-filter=company.com",
"--zone-id-filter=/hostedzone/ZTST1",
"--zone-id-filter=/hostedzone/ZTST2",
"--aws-zone-type=private", "--aws-zone-type=private",
"--policy=upsert-only", "--policy=upsert-only",
"--registry=noop", "--registry=noop",
@ -173,6 +177,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0", "EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0",
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com", "EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com", "EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
"EXTERNAL_DNS_ZONE_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2",
"EXTERNAL_DNS_AWS_ZONE_TYPE": "private", "EXTERNAL_DNS_AWS_ZONE_TYPE": "private",
"EXTERNAL_DNS_POLICY": "upsert-only", "EXTERNAL_DNS_POLICY": "upsert-only",
"EXTERNAL_DNS_REGISTRY": "noop", "EXTERNAL_DNS_REGISTRY": "noop",

View File

@ -71,12 +71,14 @@ type AWSProvider struct {
dryRun bool dryRun bool
// only consider hosted zones managing domains ending in this suffix // only consider hosted zones managing domains ending in this suffix
domainFilter DomainFilter domainFilter DomainFilter
// filter hosted zones by id
zoneIDFilter ZoneIDFilter
// filter hosted zones by type (e.g. private or public) // filter hosted zones by type (e.g. private or public)
zoneTypeFilter ZoneTypeFilter zoneTypeFilter ZoneTypeFilter
} }
// NewAWSProvider initializes a new AWS Route53 based Provider. // NewAWSProvider initializes a new AWS Route53 based Provider.
func NewAWSProvider(domainFilter DomainFilter, zoneTypeFilter ZoneTypeFilter, dryRun bool) (*AWSProvider, error) { func NewAWSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, dryRun bool) (*AWSProvider, error) {
config := aws.NewConfig() config := aws.NewConfig()
config = config.WithHTTPClient( config = config.WithHTTPClient(
@ -99,6 +101,7 @@ func NewAWSProvider(domainFilter DomainFilter, zoneTypeFilter ZoneTypeFilter, dr
provider := &AWSProvider{ provider := &AWSProvider{
client: route53.New(session), client: route53.New(session),
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
zoneTypeFilter: zoneTypeFilter, zoneTypeFilter: zoneTypeFilter,
dryRun: dryRun, dryRun: dryRun,
} }
@ -112,6 +115,10 @@ func (p *AWSProvider) Zones() (map[string]*route53.HostedZone, error) {
f := func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool) { f := func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool) {
for _, zone := range resp.HostedZones { for _, zone := range resp.HostedZones {
if !p.zoneIDFilter.Match(aws.StringValue(zone.Id)) {
continue
}
if !p.zoneTypeFilter.Match(zone) { if !p.zoneTypeFilter.Match(zone) {
continue continue
} }

View File

@ -183,15 +183,17 @@ func TestAWSZones(t *testing.T) {
for _, ti := range []struct { for _, ti := range []struct {
msg string msg string
zoneIDFilter ZoneIDFilter
zoneTypeFilter ZoneTypeFilter zoneTypeFilter ZoneTypeFilter
expectedZones map[string]*route53.HostedZone expectedZones map[string]*route53.HostedZone
}{ }{
{"no filter", NewZoneTypeFilter(""), allZones}, {"no filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), allZones},
{"public filter", NewZoneTypeFilter("public"), publicZones}, {"public filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("public"), publicZones},
{"private filter", NewZoneTypeFilter("private"), privateZones}, {"private filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("private"), privateZones},
{"unknown filter", NewZoneTypeFilter("unknown"), noZones}, {"unknown filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("unknown"), noZones},
{"zone id filter", NewZoneIDFilter([]string{"/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), privateZones},
} { } {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneTypeFilter, false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneIDFilter, ti.zoneTypeFilter, false, []*endpoint.Endpoint{})
zones, err := provider.Zones() zones, err := provider.Zones()
require.NoError(t, err) require.NoError(t, err)
@ -201,7 +203,7 @@ func TestAWSZones(t *testing.T) {
} }
func TestAWSRecords(t *testing.T) { func TestAWSRecords(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{ provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
@ -223,7 +225,7 @@ func TestAWSRecords(t *testing.T) {
func TestAWSCreateRecords(t *testing.T) { func TestAWSCreateRecords(t *testing.T) {
customTTL := endpoint.TTL(60) customTTL := endpoint.TTL(60)
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA), endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
@ -246,7 +248,7 @@ func TestAWSCreateRecords(t *testing.T) {
} }
func TestAWSUpdateRecords(t *testing.T) { func TestAWSUpdateRecords(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{ provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
@ -284,7 +286,7 @@ func TestAWSDeleteRecords(t *testing.T) {
endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME), endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
} }
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, originalEndpoints) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, originalEndpoints)
require.NoError(t, provider.DeleteRecords(originalEndpoints)) require.NoError(t, provider.DeleteRecords(originalEndpoints))
@ -296,7 +298,7 @@ func TestAWSDeleteRecords(t *testing.T) {
} }
func TestAWSApplyChanges(t *testing.T) { func TestAWSApplyChanges(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{ provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
@ -370,7 +372,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) {
endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)), endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
} }
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), true, originalEndpoints) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), true, originalEndpoints)
createRecords := []*endpoint.Endpoint{ createRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA), endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
@ -492,7 +494,7 @@ func TestAWSChangesByZones(t *testing.T) {
} }
func TestAWSsubmitChanges(t *testing.T) { func TestAWSsubmitChanges(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
const subnets = 16 const subnets = 16
const hosts = maxChangeCount / subnets const hosts = maxChangeCount / subnets
@ -604,7 +606,7 @@ func validateAWSChangeRecord(t *testing.T, record *route53.Change, expected *rou
} }
func TestAWSCreateRecordsWithCNAME(t *testing.T) { func TestAWSCreateRecordsWithCNAME(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
{DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.example.org", RecordType: endpoint.RecordTypeCNAME}, {DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.example.org", RecordType: endpoint.RecordTypeCNAME},
@ -629,7 +631,7 @@ func TestAWSCreateRecordsWithCNAME(t *testing.T) {
} }
func TestAWSCreateRecordsWithALIAS(t *testing.T) { func TestAWSCreateRecordsWithALIAS(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
{DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.eu-central-1.elb.amazonaws.com", RecordType: endpoint.RecordTypeCNAME}, {DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.eu-central-1.elb.amazonaws.com", RecordType: endpoint.RecordTypeCNAME},
@ -764,12 +766,13 @@ func clearAWSRecords(t *testing.T, provider *AWSProvider, zone string) {
} }
} }
func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneTypeFilter ZoneTypeFilter, dryRun bool, records []*endpoint.Endpoint) *AWSProvider { func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, dryRun bool, records []*endpoint.Endpoint) *AWSProvider {
client := NewRoute53APIStub() client := NewRoute53APIStub()
provider := &AWSProvider{ provider := &AWSProvider{
client: client, client: client,
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
zoneTypeFilter: zoneTypeFilter, zoneTypeFilter: zoneTypeFilter,
dryRun: false, dryRun: false,
} }

View File

@ -66,6 +66,7 @@ type RecordsClient interface {
// AzureProvider implements the DNS provider for Microsoft's Azure cloud platform. // AzureProvider implements the DNS provider for Microsoft's Azure cloud platform.
type AzureProvider struct { type AzureProvider struct {
domainFilter DomainFilter domainFilter DomainFilter
zoneIDFilter ZoneIDFilter
dryRun bool dryRun bool
resourceGroup string resourceGroup string
zonesClient ZonesClient zonesClient ZonesClient
@ -75,7 +76,7 @@ type AzureProvider struct {
// NewAzureProvider creates a new Azure provider. // NewAzureProvider creates a new Azure provider.
// //
// Returns the provider or an error if a provider could not be created. // Returns the provider or an error if a provider could not be created.
func NewAzureProvider(configFile string, domainFilter DomainFilter, resourceGroup string, dryRun bool) (*AzureProvider, error) { func NewAzureProvider(configFile string, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, resourceGroup string, dryRun bool) (*AzureProvider, error) {
contents, err := ioutil.ReadFile(configFile) contents, err := ioutil.ReadFile(configFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err) return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
@ -118,6 +119,7 @@ func NewAzureProvider(configFile string, domainFilter DomainFilter, resourceGrou
provider := &AzureProvider{ provider := &AzureProvider{
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
resourceGroup: cfg.ResourceGroup, resourceGroup: cfg.ResourceGroup,
zonesClient: zonesClient, zonesClient: zonesClient,
@ -194,9 +196,19 @@ func (p *AzureProvider) zones() ([]dns.Zone, error) {
for list.Value != nil && len(*list.Value) > 0 { for list.Value != nil && len(*list.Value) > 0 {
for _, zone := range *list.Value { for _, zone := range *list.Value {
if zone.Name != nil && p.domainFilter.Match(*zone.Name) { if zone.Name == nil {
zones = append(zones, zone) continue
} }
if !p.domainFilter.Match(*zone.Name) {
continue
}
if !p.zoneIDFilter.Match(*zone.ID) {
continue
}
zones = append(zones, zone)
} }
list, err = p.zonesClient.ListByResourceGroupNextResults(list) list, err = p.zonesClient.ListByResourceGroupNextResults(list)

View File

@ -37,8 +37,9 @@ type mockRecordsClient struct {
updatedEndpoints []*endpoint.Endpoint updatedEndpoints []*endpoint.Endpoint
} }
func createMockZone(zone string) dns.Zone { func createMockZone(zone string, id string) dns.Zone {
return dns.Zone{ return dns.Zone{
ID: to.StringPtr(id),
Name: to.StringPtr(zone), Name: to.StringPtr(zone),
} }
} }
@ -138,9 +139,10 @@ func (client *mockRecordsClient) CreateOrUpdate(resourceGroupName string, zoneNa
return parameters, nil return parameters, nil
} }
func newAzureProvider(domainFilter DomainFilter, dryRun bool, resourceGroup string, zonesClient ZonesClient, recordsClient RecordsClient) *AzureProvider { func newAzureProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, resourceGroup string, zonesClient ZonesClient, recordsClient RecordsClient) *AzureProvider {
return &AzureProvider{ return &AzureProvider{
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
resourceGroup: resourceGroup, resourceGroup: resourceGroup,
zonesClient: zonesClient, zonesClient: zonesClient,
@ -152,7 +154,7 @@ func TestAzureRecord(t *testing.T) {
zonesClient := mockZonesClient{ zonesClient := mockZonesClient{
mockZoneListResult: &dns.ZoneListResult{ mockZoneListResult: &dns.ZoneListResult{
Value: &[]dns.Zone{ Value: &[]dns.Zone{
createMockZone("example.com"), createMockZone("example.com", "/dnszones/example.com"),
}, },
}, },
} }
@ -169,7 +171,7 @@ func TestAzureRecord(t *testing.T) {
}, },
} }
provider := newAzureProvider(NewDomainFilter([]string{"example.com"}), true, "k8s", &zonesClient, &recordsClient) provider := newAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", &zonesClient, &recordsClient)
actual, err := provider.Records() actual, err := provider.Records()
if err != nil { if err != nil {
@ -225,13 +227,14 @@ func TestAzureApplyChangesDryRun(t *testing.T) {
func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordsClient) { func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordsClient) {
provider := newAzureProvider( provider := newAzureProvider(
NewDomainFilter([]string{""}), NewDomainFilter([]string{""}),
NewZoneIDFilter([]string{""}),
dryRun, dryRun,
"group", "group",
&mockZonesClient{ &mockZonesClient{
mockZoneListResult: &dns.ZoneListResult{ mockZoneListResult: &dns.ZoneListResult{
Value: &[]dns.Zone{ Value: &[]dns.Zone{
createMockZone("example.com"), createMockZone("example.com", "/dnszones/example.com"),
createMockZone("other.com"), createMockZone("other.com", "/dnszones/other.com"),
}, },
}, },
}, },

View File

@ -92,6 +92,7 @@ type CloudFlareProvider struct {
Client cloudFlareDNS Client cloudFlareDNS
// only consider hosted zones managing domains ending in this suffix // only consider hosted zones managing domains ending in this suffix
domainFilter DomainFilter domainFilter DomainFilter
zoneIDFilter ZoneIDFilter
proxied bool proxied bool
DryRun bool DryRun bool
} }
@ -103,7 +104,7 @@ type cloudFlareChange struct {
} }
// NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider.
func NewCloudFlareProvider(domainFilter DomainFilter, proxied bool, dryRun bool) (*CloudFlareProvider, error) { func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, proxied bool, dryRun bool) (*CloudFlareProvider, error) {
// initialize via API email and API key and returns new API object // initialize via API email and API key and returns new API object
config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
if err != nil { if err != nil {
@ -113,6 +114,7 @@ func NewCloudFlareProvider(domainFilter DomainFilter, proxied bool, dryRun bool)
//Client: config, //Client: config,
Client: zoneService{config}, Client: zoneService{config},
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
proxied: proxied, proxied: proxied,
DryRun: dryRun, DryRun: dryRun,
} }
@ -129,9 +131,15 @@ func (p *CloudFlareProvider) Zones() ([]cloudflare.Zone, error) {
} }
for _, zone := range zones { for _, zone := range zones {
if p.domainFilter.Match(zone.Name) { if !p.domainFilter.Match(zone.Name) {
result = append(result, zone) continue
} }
if !p.zoneIDFilter.Match(zone.ID) {
continue
}
result = append(result, zone)
} }
return result, nil return result, nil

View File

@ -378,6 +378,7 @@ func TestCloudFlareZones(t *testing.T) {
provider := &CloudFlareProvider{ provider := &CloudFlareProvider{
Client: &mockCloudFlareClient{}, Client: &mockCloudFlareClient{},
domainFilter: NewDomainFilter([]string{"zalando.to."}), domainFilter: NewDomainFilter([]string{"zalando.to."}),
zoneIDFilter: NewZoneIDFilter([]string{""}),
} }
zones, err := provider.Zones() zones, err := provider.Zones()
@ -415,13 +416,13 @@ func TestRecords(t *testing.T) {
func TestNewCloudFlareProvider(t *testing.T) { func TestNewCloudFlareProvider(t *testing.T) {
_ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx") _ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx")
_ = os.Setenv("CF_API_EMAIL", "test@test.com") _ = os.Setenv("CF_API_EMAIL", "test@test.com")
_, err := NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), false, true) _, err := NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true)
if err != nil { if err != nil {
t.Errorf("should not fail, %s", err) t.Errorf("should not fail, %s", err)
} }
_ = os.Unsetenv("CF_API_KEY") _ = os.Unsetenv("CF_API_KEY")
_ = os.Unsetenv("CF_API_EMAIL") _ = os.Unsetenv("CF_API_EMAIL")
_, err = NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), false, true) _, err = NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true)
if err == nil { if err == nil {
t.Errorf("expected to fail") t.Errorf("expected to fail")
} }

View File

@ -84,6 +84,7 @@ type dnsimpleProvider struct {
identity identityService identity identityService
accountID string accountID string
domainFilter DomainFilter domainFilter DomainFilter
zoneIDFilter ZoneIDFilter
dryRun bool dryRun bool
} }
@ -99,7 +100,7 @@ const (
) )
// NewDnsimpleProvider initializes a new Dnsimple based provider // NewDnsimpleProvider initializes a new Dnsimple based provider
func NewDnsimpleProvider(domainFilter DomainFilter, dryRun bool) (Provider, error) { func NewDnsimpleProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool) (Provider, error) {
oauthToken := os.Getenv("DNSIMPLE_OAUTH") oauthToken := os.Getenv("DNSIMPLE_OAUTH")
if len(oauthToken) == 0 { if len(oauthToken) == 0 {
return nil, fmt.Errorf("No dnsimple oauth token provided") return nil, fmt.Errorf("No dnsimple oauth token provided")
@ -109,6 +110,7 @@ func NewDnsimpleProvider(domainFilter DomainFilter, dryRun bool) (Provider, erro
client: dnsimpleZoneService{service: client.Zones}, client: dnsimpleZoneService{service: client.Zones},
identity: identityService{service: client.Identity}, identity: identityService{service: client.Identity},
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
} }
whoamiResponse, err := provider.identity.service.Whoami() whoamiResponse, err := provider.identity.service.Whoami()
@ -119,7 +121,7 @@ func NewDnsimpleProvider(domainFilter DomainFilter, dryRun bool) (Provider, erro
return provider, nil return provider, nil
} }
// Returns a list of Zones that end with the provider's domainFilter // Returns a list of filtered Zones
func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) { func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) {
zones := make(map[string]dnsimple.Zone) zones := make(map[string]dnsimple.Zone)
zonesResponse, err := p.client.ListZones(p.accountID, &dnsimple.ZoneListOptions{}) zonesResponse, err := p.client.ListZones(p.accountID, &dnsimple.ZoneListOptions{})
@ -127,9 +129,15 @@ func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) {
return nil, err return nil, err
} }
for _, zone := range zonesResponse.Data { for _, zone := range zonesResponse.Data {
if p.domainFilter.Match(zone.Name) { if !p.domainFilter.Match(zone.Name) {
zones[strconv.Itoa(zone.ID)] = zone continue
} }
if !p.zoneIDFilter.Match(strconv.Itoa(zone.ID)) {
continue
}
zones[strconv.Itoa(zone.ID)] = zone
} }
return zones, nil return zones, nil
} }

View File

@ -153,7 +153,7 @@ func testDnsimpleSuitableZone(t *testing.T) {
func TestNewDnsimpleProvider(t *testing.T) { func TestNewDnsimpleProvider(t *testing.T) {
os.Setenv("DNSIMPLE_OAUTH", "xxxxxxxxxxxxxxxxxxxxxxxxxx") os.Setenv("DNSIMPLE_OAUTH", "xxxxxxxxxxxxxxxxxxxxxxxxxx")
_, err := NewDnsimpleProvider(DomainFilter{filters: []string{"example.com"}}, true) _, err := NewDnsimpleProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true)
if err == nil { if err == nil {
t.Errorf("Expected to fail new provider on bad token") t.Errorf("Expected to fail new provider on bad token")
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package provider package provider
import ( import (
"fmt"
"strings" "strings"
"github.com/linki/instrumented_http" "github.com/linki/instrumented_http"
@ -102,6 +103,8 @@ type GoogleProvider struct {
dryRun bool dryRun bool
// only consider hosted zones managing domains ending in this suffix // only consider hosted zones managing domains ending in this suffix
domainFilter DomainFilter domainFilter DomainFilter
// only consider hosted zones ending with this zone id
zoneIDFilter ZoneIDFilter
// A client for managing resource record sets // A client for managing resource record sets
resourceRecordSetsClient resourceRecordSetsClientInterface resourceRecordSetsClient resourceRecordSetsClientInterface
// A client for managing hosted zones // A client for managing hosted zones
@ -111,7 +114,7 @@ type GoogleProvider struct {
} }
// NewGoogleProvider initializes a new Google CloudDNS based Provider. // NewGoogleProvider initializes a new Google CloudDNS based Provider.
func NewGoogleProvider(project string, domainFilter DomainFilter, dryRun bool) (*GoogleProvider, error) { func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool) (*GoogleProvider, error) {
gcloud, err := google.DefaultClient(context.TODO(), dns.NdevClouddnsReadwriteScope) gcloud, err := google.DefaultClient(context.TODO(), dns.NdevClouddnsReadwriteScope)
if err != nil { if err != nil {
return nil, err return nil, err
@ -132,6 +135,7 @@ func NewGoogleProvider(project string, domainFilter DomainFilter, dryRun bool) (
provider := &GoogleProvider{ provider := &GoogleProvider{
project: project, project: project,
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
resourceRecordSetsClient: resourceRecordSetsService{dnsClient.ResourceRecordSets}, resourceRecordSetsClient: resourceRecordSetsService{dnsClient.ResourceRecordSets},
managedZonesClient: managedZonesService{dnsClient.ManagedZones}, managedZonesClient: managedZonesService{dnsClient.ManagedZones},
@ -147,9 +151,15 @@ func (p *GoogleProvider) Zones() (map[string]*dns.ManagedZone, error) {
f := func(resp *dns.ManagedZonesListResponse) error { f := func(resp *dns.ManagedZonesListResponse) error {
for _, zone := range resp.ManagedZones { for _, zone := range resp.ManagedZones {
if p.domainFilter.Match(zone.DnsName) { if !p.domainFilter.Match(zone.DnsName) {
zones[zone.Name] = zone continue
} }
if !p.zoneIDFilter.Match(fmt.Sprintf("%v", zone.Id)) {
continue
}
zones[zone.Name] = zone
} }
return nil return nil

View File

@ -193,7 +193,7 @@ func hasTrailingDot(target string) bool {
} }
func TestGoogleZones(t *testing.T) { func TestGoogleZones(t *testing.T) {
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, []*endpoint.Endpoint{}) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
zones, err := provider.Zones() zones, err := provider.Zones()
require.NoError(t, err) require.NoError(t, err)
@ -212,7 +212,7 @@ func TestGoogleRecords(t *testing.T) {
endpoint.NewEndpoint("list-test-alias.zone-1.ext-dns-test-2.gcp.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME), endpoint.NewEndpoint("list-test-alias.zone-1.ext-dns-test-2.gcp.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
} }
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, originalEndpoints) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, originalEndpoints)
records, err := provider.Records() records, err := provider.Records()
require.NoError(t, err) require.NoError(t, err)
@ -221,7 +221,7 @@ func TestGoogleRecords(t *testing.T) {
} }
func TestGoogleCreateRecords(t *testing.T) { func TestGoogleCreateRecords(t *testing.T) {
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, []*endpoint.Endpoint{}) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", "1.2.3.4", endpoint.RecordTypeA), endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
@ -242,7 +242,7 @@ func TestGoogleCreateRecords(t *testing.T) {
} }
func TestGoogleUpdateRecords(t *testing.T) { func TestGoogleUpdateRecords(t *testing.T) {
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, []*endpoint.Endpoint{ provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA), endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", "8.8.4.4", endpoint.RecordTypeA), endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME), endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
@ -278,7 +278,7 @@ func TestGoogleDeleteRecords(t *testing.T) {
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME), endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME),
} }
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, originalEndpoints) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, originalEndpoints)
require.NoError(t, provider.DeleteRecords(originalEndpoints)) require.NoError(t, provider.DeleteRecords(originalEndpoints))
@ -289,7 +289,7 @@ func TestGoogleDeleteRecords(t *testing.T) {
} }
func TestGoogleApplyChanges(t *testing.T) { func TestGoogleApplyChanges(t *testing.T) {
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, []*endpoint.Endpoint{ provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA), endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA), endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", "8.8.4.4", endpoint.RecordTypeA), endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
@ -353,7 +353,7 @@ func TestGoogleApplyChangesDryRun(t *testing.T) {
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME), endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME),
} }
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), true, originalEndpoints) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), true, originalEndpoints)
createRecords := []*endpoint.Endpoint{ createRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA), endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
@ -394,7 +394,7 @@ func TestGoogleApplyChangesDryRun(t *testing.T) {
} }
func TestGoogleApplyChangesEmpty(t *testing.T) { func TestGoogleApplyChangesEmpty(t *testing.T) {
provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), false, []*endpoint.Endpoint{}) provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
assert.NoError(t, provider.ApplyChanges(&plan.Changes{})) assert.NoError(t, provider.ApplyChanges(&plan.Changes{}))
} }
@ -501,10 +501,11 @@ func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected
assert.Equal(t, expected.Type, record.Type) assert.Equal(t, expected.Type, record.Type)
} }
func newGoogleProvider(t *testing.T, domainFilter DomainFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider { func newGoogleProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider {
provider := &GoogleProvider{ provider := &GoogleProvider{
project: "zalando-external-dns-test", project: "zalando-external-dns-test",
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: false, dryRun: false,
resourceRecordSetsClient: &mockResourceRecordSetsClient{}, resourceRecordSetsClient: &mockResourceRecordSetsClient{},
managedZonesClient: &mockManagedZonesClient{}, managedZonesClient: &mockManagedZonesClient{},

View File

@ -30,6 +30,7 @@ import (
// InfobloxConfig clarifies the method signature // InfobloxConfig clarifies the method signature
type InfobloxConfig struct { type InfobloxConfig struct {
DomainFilter DomainFilter DomainFilter DomainFilter
ZoneIDFilter ZoneIDFilter
Host string Host string
Port int Port int
Username string Username string
@ -43,6 +44,7 @@ type InfobloxConfig struct {
type InfobloxProvider struct { type InfobloxProvider struct {
client ibclient.IBConnector client ibclient.IBConnector
domainFilter DomainFilter domainFilter DomainFilter
zoneIDFilter ZoneIDFilter
dryRun bool dryRun bool
} }
@ -82,6 +84,7 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro
provider := &InfobloxProvider{ provider := &InfobloxProvider{
client: client, client: client,
domainFilter: infobloxConfig.DomainFilter, domainFilter: infobloxConfig.DomainFilter,
zoneIDFilter: infobloxConfig.ZoneIDFilter,
dryRun: infobloxConfig.DryRun, dryRun: infobloxConfig.DryRun,
} }
@ -186,9 +189,15 @@ func (p *InfobloxProvider) zones() ([]ibclient.ZoneAuth, error) {
} }
for _, zone := range res { for _, zone := range res {
if p.domainFilter.Match(zone.Fqdn) { if !p.domainFilter.Match(zone.Fqdn) {
result = append(result, zone) continue
} }
if !p.zoneIDFilter.Match(zone.Ref) {
continue
}
result = append(result, zone)
} }
return result, nil return result, nil

View File

@ -327,10 +327,11 @@ func createMockInfobloxObject(name, recordType, value string) ibclient.IBObject
return nil return nil
} }
func newInfobloxProvider(domainFilter DomainFilter, dryRun bool, client ibclient.IBConnector) *InfobloxProvider { func newInfobloxProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, client ibclient.IBConnector) *InfobloxProvider {
return &InfobloxProvider{ return &InfobloxProvider{
client: client, client: client,
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
} }
} }
@ -351,7 +352,7 @@ func TestInfobloxRecords(t *testing.T) {
}, },
} }
provider := newInfobloxProvider(NewDomainFilter([]string{"example.com"}), true, &client) provider := newInfobloxProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, &client)
actual, err := provider.Records() actual, err := provider.Records()
if err != nil { if err != nil {
@ -425,6 +426,7 @@ func testInfobloxApplyChangesInternal(t *testing.T, dryRun bool, client ibclient
provider := newInfobloxProvider( provider := newInfobloxProvider(
NewDomainFilter([]string{""}), NewDomainFilter([]string{""}),
NewZoneIDFilter([]string{""}),
dryRun, dryRun,
client, client,
) )
@ -482,7 +484,7 @@ func TestInfobloxZones(t *testing.T) {
mockInfobloxObjects: &[]ibclient.IBObject{}, mockInfobloxObjects: &[]ibclient.IBObject{},
} }
provider := newInfobloxProvider(NewDomainFilter([]string{"example.com"}), true, &client) provider := newInfobloxProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, &client)
zones, _ := provider.zones() zones, _ := provider.zones()
assert.Equal(t, provider.findZone(zones, "example.com").Fqdn, "example.com") assert.Equal(t, provider.findZone(zones, "example.com").Fqdn, "example.com")

View File

@ -0,0 +1,45 @@
/*
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 "strings"
// ZoneIDFilter holds a list of zone ids to filter by
type ZoneIDFilter struct {
zoneIDs []string
}
// NewZoneIDFilter returns a new ZoneIDFilter given a list of zone ids
func NewZoneIDFilter(zoneIDs []string) ZoneIDFilter {
return ZoneIDFilter{zoneIDs}
}
// Match checks whether a zone matches one of the provided zone ids
func (f ZoneIDFilter) Match(zoneID string) bool {
// An empty filter includes all zones.
if len(f.zoneIDs) == 0 {
return true
}
for _, id := range f.zoneIDs {
if strings.HasSuffix(zoneID, id) {
return true
}
}
return false
}

View File

@ -0,0 +1,79 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
)
type zoneIDFilterTest struct {
zoneIDFilter []string
zone string
expected bool
}
func TestZoneIDFilterMatch(t *testing.T) {
zone := "/hostedzone/ZTST1"
for _, tt := range []zoneIDFilterTest{
{
[]string{},
zone,
true,
},
{
[]string{"/hostedzone/ZTST1"},
zone,
true,
},
{
[]string{"/hostedzone/ZTST2"},
zone,
false,
},
{
[]string{"ZTST1"},
zone,
true,
},
{
[]string{"ZTST2"},
zone,
false,
},
{
[]string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
zone,
true,
},
{
[]string{"/hostedzone/ZTST2", "/hostedzone/ZTST3"},
zone,
false,
},
{
[]string{"/hostedzone/ZTST2", "/hostedzone/ZTST1"},
zone,
true,
},
} {
zoneIDFilter := NewZoneIDFilter(tt.zoneIDFilter)
assert.Equal(t, tt.expected, zoneIDFilter.Match(tt.zone))
}
}