From 7c14d767c5db9b1f595d5df04662cceb80d32866 Mon Sep 17 00:00:00 2001 From: Quinton Hoole Date: Sun, 22 May 2016 21:40:22 -0700 Subject: [PATCH] AWS Route53 dnsprovider --- .../providers/aws/route53/interface.go | 39 +++ .../providers/aws/route53/route53.go | 44 ++++ .../providers/aws/route53/route53_test.go | 236 ++++++++++++++++++ .../providers/aws/route53/rrset.go | 53 ++++ .../providers/aws/route53/rrsets.go | 135 ++++++++++ .../aws/route53/testing/route53api.go | 112 +++++++++ .../dnsprovider/providers/aws/route53/zone.go | 38 +++ .../providers/aws/route53/zones.go | 45 ++++ 8 files changed, 702 insertions(+) create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/interface.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/route53.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/route53_test.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/rrset.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/rrsets.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/testing/route53api.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/zone.go create mode 100644 federation/pkg/dnsprovider/providers/aws/route53/zones.go diff --git a/federation/pkg/dnsprovider/providers/aws/route53/interface.go b/federation/pkg/dnsprovider/providers/aws/route53/interface.go new file mode 100644 index 00000000000..e394b0d7daf --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/interface.go @@ -0,0 +1,39 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "k8s.io/kubernetes/federation/pkg/dnsprovider" + "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/testing" +) + +// Compile time check for interface adeherence +var _ dnsprovider.Interface = Interface{} + +type Interface struct { + service testing.Route53API +} + +// newInterfaceWithStub facilitates stubbing out the underlying AWS Route53 +// library for testing purposes. It returns an provider-independent interface. +func newInterfaceWithStub(service testing.Route53API) *Interface { + return &Interface{service} +} + +func (i Interface) Zones() (zones dnsprovider.Zones, supported bool) { + return Zones{&i}, true +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/route53.go b/federation/pkg/dnsprovider/providers/aws/route53/route53.go new file mode 100644 index 00000000000..fa8a11be0fe --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/route53.go @@ -0,0 +1,44 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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. +*/ + +// route53 is the implementation of pkg/dnsprovider interface for AWS Route53 +package route53 + +import ( + "io" + + "k8s.io/kubernetes/federation/pkg/dnsprovider" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/route53" +) + +const ( + ProviderName = "aws-route53" +) + +func init() { + dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) { + return newRoute53(config) + }) +} + +// newRoute53 creates a new instance of an AWS Route53 DNS Interface. +func newRoute53(config io.Reader) (*Interface, error) { + // Connect to AWS Route53 - TODO: Do more sophisticated auth + svc := route53.New(session.New()) + return newInterfaceWithStub(svc), nil +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/route53_test.go b/federation/pkg/dnsprovider/providers/aws/route53/route53_test.go new file mode 100644 index 00000000000..bb72ed933c6 --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/route53_test.go @@ -0,0 +1,236 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "flag" + "fmt" + "os" + "testing" + + "k8s.io/kubernetes/federation/pkg/dnsprovider" + route53testing "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/testing" + "k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" +) + +func newTestInterface() (dnsprovider.Interface, error) { + // Use this to test the real cloud service. + // i, err := dnsprovider.GetDnsProvider(ProviderName, strings.NewReader("\n[global]\nproject-id = federation0-cluster00")) + return newFakeInterface() // Use this to stub out the entire cloud service +} + +func newFakeInterface() (dnsprovider.Interface, error) { + var service route53testing.Route53API + service = route53testing.NewRoute53APIStub() + iface := newInterfaceWithStub(service) + // Add a fake zone to test against. + params := &route53.CreateHostedZoneInput{ + CallerReference: aws.String("Nonce"), // Required + Name: aws.String("example.com"), // Required + } + _, err := iface.service.CreateHostedZone(params) + if err != nil { + return nil, err + } + return iface, nil +} + +var interface_ dnsprovider.Interface + +func TestMain(m *testing.M) { + fmt.Printf("Parsing flags.\n") + flag.Parse() + var err error + fmt.Printf("Getting new test interface.\n") + interface_, err = newTestInterface() + if err != nil { + fmt.Printf("Error creating interface: %v", err) + os.Exit(1) + } + fmt.Printf("Running tests...\n") + os.Exit(m.Run()) +} + +// firstZone returns the first zone for the configured dns provider account/project, +// or fails if it can't be found +func firstZone(t *testing.T) dnsprovider.Zone { + t.Logf("Getting zones") + z, supported := interface_.Zones() + if supported { + t.Logf("Got zones %v\n", z) + } else { + t.Fatalf("Zones interface not supported by interface %v", interface_) + } + zones, err := z.List() + if err != nil { + t.Fatalf("Failed to list zones: %v", err) + } else { + t.Logf("Got zone list: %v\n", zones) + } + if len(zones) < 1 { + t.Fatalf("Zone listing returned %d, expected >= %d", len(zones), 1) + } else { + t.Logf("Got at least 1 zone in list:%v\n", zones[0]) + } + return zones[0] +} + +/* rrs returns the ResourceRecordSets interface for a given zone */ +func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) { + rrsets, supported := zone.ResourceRecordSets() + if !supported { + t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone) + return r + } + return rrsets +} + +func listRrsOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets) []dnsprovider.ResourceRecordSet { + rrset, err := rrsets.List() + if err != nil { + t.Fatalf("Failed to list recordsets: %v", err) + } else { + if len(rrset) < 0 { + t.Fatalf("Record set length=%d, expected >=0", len(rrset)) + } else { + t.Logf("Got %d recordsets: %v", len(rrset), rrset) + } + } + return rrset +} + +func getExampleRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet { + rrsets, _ := zone.ResourceRecordSets() + return rrsets.New("www11."+zone.Name(), []string{"10.10.10.10", "169.20.20.20"}, 180, rrstype.A) +} + +func getInvalidRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet { + rrsets, _ := zone.ResourceRecordSets() + return rrsets.New("www12."+zone.Name(), []string{"rubbish", "rubbish"}, 180, rrstype.A) +} + +func addRrsetOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordSet { + result, err := rrsets.Add(rrset) + if err != nil { + t.Fatalf("Failed to add recordsets: %v", err) + } + return result +} + +/* TestResourceRecordSetsList verifies that listing of zones succeeds */ +func TestZonesList(t *testing.T) { + firstZone(t) +} + +/* TestResourceRecordSetsList verifies that listing of RRS's succeeds */ +func TestResourceRecordSetsList(t *testing.T) { + listRrsOrFail(t, rrs(t, firstZone(t))) +} + +/* TestResourceRecordSetsAddSuccess verifies that addition of a valid RRS succeeds */ +func TestResourceRecordSetsAddSuccess(t *testing.T) { + zone := firstZone(t) + sets := rrs(t, zone) + set := addRrsetOrFail(t, sets, getExampleRrs(zone)) + defer sets.Remove(set) + t.Logf("Successfully added resource record set: %v", set) +} + +/* TestResourceRecordSetsAdditionVisible verifies that added RRS is visible after addition */ +func TestResourceRecordSetsAdditionVisible(t *testing.T) { + zone := firstZone(t) + sets := rrs(t, zone) + rrset := getExampleRrs(zone) + set := addRrsetOrFail(t, sets, rrset) + defer sets.Remove(set) + t.Logf("Successfully added resource record set: %v", set) + found := false + for _, record := range listRrsOrFail(t, sets) { + if record.Name() == rrset.Name() { + found = true + break + } + } + if !found { + t.Errorf("Failed to find added resource record set %s", rrset.Name()) + } +} + +/* TestResourceRecordSetsAddDuplicateFail verifies that addition of a duplicate RRS fails */ +func TestResourceRecordSetsAddDuplicateFail(t *testing.T) { + zone := firstZone(t) + sets := rrs(t, zone) + rrset := getExampleRrs(zone) + set := addRrsetOrFail(t, sets, rrset) + defer sets.Remove(set) + t.Logf("Successfully added resource record set: %v", set) + // Try to add it again, and verify that the call fails. + rrs, err := sets.Add(rrset) + if err == nil { + defer sets.Remove(rrs) + t.Errorf("Should have failed to add duplicate resource record %v, but succeeded instead.", set) + } else { + t.Logf("Correctly failed to add duplicate resource record %v: %v", set, err) + } +} + +/* TestResourceRecordSetsRemove verifies that the removal of an existing RRS succeeds */ +func TestResourceRecordSetsRemove(t *testing.T) { + zone := firstZone(t) + sets := rrs(t, zone) + rrset := getExampleRrs(zone) + set := addRrsetOrFail(t, sets, rrset) + err := sets.Remove(set) + if err != nil { + // Try again to clean up. + defer sets.Remove(rrset) + t.Errorf("Failed to remove resource record set %v after adding", rrset) + } else { + t.Logf("Successfully removed resource set %v after adding", set) + } +} + +/* TestResourceRecordSetsRemoveGone verifies that a removed RRS no longer exists */ +func TestResourceRecordSetsRemoveGone(t *testing.T) { + zone := firstZone(t) + sets := rrs(t, zone) + rrset := getExampleRrs(zone) + set := addRrsetOrFail(t, sets, rrset) + err := sets.Remove(set) + if err != nil { + // Try again to clean up. + defer sets.Remove(rrset) + t.Errorf("Failed to remove resource record set %v after adding", rrset) + } else { + t.Logf("Successfully removed resource set %v after adding", set) + } + // Check that it's gone + list := listRrsOrFail(t, sets) + found := false + for _, set := range list { + if set.Name() == rrset.Name() { + found = true + break + } + } + if found { + t.Errorf("Deleted resource record set %v is still present", rrset) + } +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/rrset.go b/federation/pkg/dnsprovider/providers/aws/route53/rrset.go new file mode 100644 index 00000000000..b762d09918f --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/rrset.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "k8s.io/kubernetes/federation/pkg/dnsprovider" + "k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype" + + "github.com/aws/aws-sdk-go/service/route53" +) + +// Compile time check for interface adeherence +var _ dnsprovider.ResourceRecordSet = ResourceRecordSet{} + +type ResourceRecordSet struct { + impl *route53.ResourceRecordSet + rrsets *ResourceRecordSets +} + +func (rrset ResourceRecordSet) Name() string { + return *rrset.impl.Name +} + +func (rrset ResourceRecordSet) Rrdatas() []string { + // Sigh - need to unpack the strings out of the route53 ResourceRecords + result := make([]string, len(rrset.impl.ResourceRecords)) + for i, record := range rrset.impl.ResourceRecords { + result[i] = *record.Value + } + return result +} + +func (rrset ResourceRecordSet) Ttl() int64 { + return *rrset.impl.TTL +} + +func (rrset ResourceRecordSet) Type() rrstype.RrsType { + return rrstype.RrsType(*rrset.impl.Type) +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/rrsets.go b/federation/pkg/dnsprovider/providers/aws/route53/rrsets.go new file mode 100644 index 00000000000..1ceb729274b --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/rrsets.go @@ -0,0 +1,135 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" + "k8s.io/kubernetes/federation/pkg/dnsprovider" + "k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype" +) + +// Compile time check for interface adeherence +var _ dnsprovider.ResourceRecordSets = ResourceRecordSets{} + +type ResourceRecordSets struct { + zone *Zone +} + +func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) { + input := route53.ListResourceRecordSetsInput{ + HostedZoneId: rrsets.zone.impl.Id, + } + response, err := rrsets.zone.zones.interface_.service.ListResourceRecordSets(&input) + // TODO: Handle truncated responses + if err != nil { + return nil, err + } + list := make([]dnsprovider.ResourceRecordSet, len(response.ResourceRecordSets)) + for i, rrset := range response.ResourceRecordSets { + list[i] = &ResourceRecordSet{rrset, &rrsets} + } + return list, nil +} + +func (rrsets ResourceRecordSets) Add(rrset dnsprovider.ResourceRecordSet) (dnsprovider.ResourceRecordSet, error) { + service := rrsets.zone.zones.interface_.service + input := getChangeResourceRecordSetsInput("CREATE", rrset.Name(), string(rrset.Type()), *rrset.(ResourceRecordSet).rrsets.zone.impl.Id, rrset.Rrdatas(), rrset.Ttl()) + _, err := service.ChangeResourceRecordSets(input) + if err != nil { + // Cast err to awserr.Error to get the Code and + // Message from an error. + return nil, err + } + return ResourceRecordSet{input.ChangeBatch.Changes[0].ResourceRecordSet, &rrsets}, nil +} + +func (rrsets ResourceRecordSets) Remove(rrset dnsprovider.ResourceRecordSet) error { + input := getChangeResourceRecordSetsInput("DELETE", rrset.Name(), string(rrset.Type()), *rrset.(ResourceRecordSet).rrsets.zone.impl.Id, rrset.Rrdatas(), rrset.Ttl()) + _, err := rrsets.zone.zones.interface_.service.ChangeResourceRecordSets(input) + if err != nil { + // Cast err to awserr.Error to get the Code and + // Message from an error. + return err + } + return nil +} + +func getChangeResourceRecordSetsInput(action, name, type_, hostedZoneId string, rrdatas []string, ttl int64) *route53.ChangeResourceRecordSetsInput { + input := &route53.ChangeResourceRecordSetsInput{ + ChangeBatch: &route53.ChangeBatch{ // Required + Changes: []*route53.Change{ // Required + { // Required + Action: aws.String(action), // Required + ResourceRecordSet: &route53.ResourceRecordSet{ // Required + Name: aws.String(name), // Required + Type: aws.String(type_), // Required + /* + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("DNSName"), // Required + EvaluateTargetHealth: aws.Bool(true), // Required + HostedZoneId: aws.String("ResourceId"), // Required + }, + Failover: aws.String("ResourceRecordSetFailover"), + GeoLocation: &route53.GeoLocation{ + ContinentCode: aws.String("GeoLocationContinentCode"), + CountryCode: aws.String("GeoLocationCountryCode"), + SubdivisionCode: aws.String("GeoLocationSubdivisionCode"), + }, + HealthCheckId: aws.String("HealthCheckId"), + Region: aws.String("ResourceRecordSetRegion"), + */ + ResourceRecords: []*route53.ResourceRecord{ + { // Required + Value: aws.String(rrdatas[0]), // Required + }, + // More values... + }, + /* + SetIdentifier: aws.String("ResourceRecordSetIdentifier"), + + */ + TTL: aws.Int64(ttl), + /* + TrafficPolicyInstanceId: aws.String("TrafficPolicyInstanceId"), + Weight: aws.Int64(1), + */ + }, + }, + // More values... + }, + }, + HostedZoneId: aws.String(hostedZoneId), // Required + } +} + +func (rrsets ResourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet { + rrstypeStr := string(rrstype) + return ResourceRecordSet{ + &route53.ResourceRecordSet{ + Name: &name, + Type: &rrstypeStr, + TTL: &ttl, + ResourceRecords: []*route53.ResourceRecord{ + { + Value: &rrdatas[0], + }, + }, + }, // TODO: Add remaining rrdatas + &rrsets, + } +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/testing/route53api.go b/federation/pkg/dnsprovider/providers/aws/route53/testing/route53api.go new file mode 100644 index 00000000000..23a1788fc74 --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/testing/route53api.go @@ -0,0 +1,112 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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. +*/ + +/* internal implements a stub for the AWS Route53 API, used primarily for unit testing purposes */ +package testing + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/route53" +) + +// Compile time check for interface conformance +var _ Route53API = &Route53APIStub{} + +/* Route53API is the subset of the AWS Route53 API that we actually use. Add methods as required. Signatures must match exactly. */ +type Route53API interface { + ListResourceRecordSets(*route53.ListResourceRecordSetsInput) (*route53.ListResourceRecordSetsOutput, error) + ChangeResourceRecordSets(*route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) + ListHostedZones(*route53.ListHostedZonesInput) (*route53.ListHostedZonesOutput, error) + CreateHostedZone(*route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) +} + +// Route53APIStub is a minimal implementation of Route53API, used primarily for unit testing. +// See http://http://docs.aws.amazon.com/sdk-for-go/api/service/route53.html for descriptions +// of all of it's methods. +type Route53APIStub struct { + zones map[string]*route53.HostedZone + recordSets map[string]map[string][]*route53.ResourceRecordSet +} + +// NewRoute53APIStub returns an initlialized Route53APIStub +func NewRoute53APIStub() *Route53APIStub { + return &Route53APIStub{ + zones: make(map[string]*route53.HostedZone), + recordSets: make(map[string]map[string][]*route53.ResourceRecordSet), + } +} + +func (r *Route53APIStub) ListResourceRecordSets(input *route53.ListResourceRecordSetsInput) (*route53.ListResourceRecordSetsOutput, error) { + output := route53.ListResourceRecordSetsOutput{} // TODO: Support optional input args. + if len(r.recordSets) <= 0 { + output.ResourceRecordSets = []*route53.ResourceRecordSet{} + } else if _, ok := r.recordSets[*input.HostedZoneId]; !ok { + output.ResourceRecordSets = []*route53.ResourceRecordSet{} + } else { + for _, rrsets := range r.recordSets[*input.HostedZoneId] { + for _, rrset := range rrsets { + output.ResourceRecordSets = append(output.ResourceRecordSets, rrset) + } + } + } + return &output, nil +} + +func (r *Route53APIStub) ChangeResourceRecordSets(input *route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) { + output := &route53.ChangeResourceRecordSetsOutput{} + recordSets, ok := r.recordSets[*input.HostedZoneId] + if !ok { + recordSets = make(map[string][]*route53.ResourceRecordSet) + } + + for _, change := range input.ChangeBatch.Changes { + switch *change.Action { + case route53.ChangeActionCreate: + if _, found := recordSets[*change.ResourceRecordSet.Name]; found { + return nil, fmt.Errorf("Attempt to create duplicate rrset %s", change.ResourceRecordSet.Name) // TODO: Return AWS errors with codes etc + } + recordSets[*change.ResourceRecordSet.Name] = append(recordSets[*change.ResourceRecordSet.Name], change.ResourceRecordSet) + case route53.ChangeActionDelete: + if _, found := recordSets[*change.ResourceRecordSet.Name]; !found { + return nil, fmt.Errorf("Attempt to delete non-existant rrset %s", change.ResourceRecordSet.Name) // TODO: Check other fields too + } + delete(recordSets, *change.ResourceRecordSet.Name) + case route53.ChangeActionUpsert: + // TODO - not used yet + } + } + r.recordSets[*input.HostedZoneId] = recordSets + return output, nil // TODO: We should ideally return status etc, but we dont' use that yet. +} + +func (r *Route53APIStub) ListHostedZones(*route53.ListHostedZonesInput) (*route53.ListHostedZonesOutput, error) { + output := &route53.ListHostedZonesOutput{} + for _, zone := range r.zones { + output.HostedZones = append(output.HostedZones, zone) + } + return output, nil +} + +func (r *Route53APIStub) CreateHostedZone(input *route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) { + if _, ok := r.zones[*input.Name]; ok { + return nil, fmt.Errorf("Error creating hosted DNS zone: %s already exists", input.Name) + } + r.zones[*input.Name] = &route53.HostedZone{ + Id: input.Name, + Name: input.Name, + } + return &route53.CreateHostedZoneOutput{HostedZone: r.zones[*input.Name]}, nil +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/zone.go b/federation/pkg/dnsprovider/providers/aws/route53/zone.go new file mode 100644 index 00000000000..9877142d02d --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/zone.go @@ -0,0 +1,38 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "github.com/aws/aws-sdk-go/service/route53" + "k8s.io/kubernetes/federation/pkg/dnsprovider" +) + +// Compile time check for interface adeherence +var _ dnsprovider.Zone = &Zone{} + +type Zone struct { + impl *route53.HostedZone + zones *Zones +} + +func (zone *Zone) Name() string { + return *zone.impl.Name +} + +func (zone *Zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) { + return &ResourceRecordSets{zone}, true +} diff --git a/federation/pkg/dnsprovider/providers/aws/route53/zones.go b/federation/pkg/dnsprovider/providers/aws/route53/zones.go new file mode 100644 index 00000000000..288147686bb --- /dev/null +++ b/federation/pkg/dnsprovider/providers/aws/route53/zones.go @@ -0,0 +1,45 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 route53 + +import ( + "github.com/aws/aws-sdk-go/service/route53" + "k8s.io/kubernetes/federation/pkg/dnsprovider" +) + +// Compile time check for interface adeherence +var _ dnsprovider.Zones = Zones{} + +type Zones struct { + interface_ *Interface +} + +func (zones Zones) List() ([]dnsprovider.Zone, error) { + input := route53.ListHostedZonesInput{} + response, err := zones.interface_.service.ListHostedZones(&input) + if err != nil { + return []dnsprovider.Zone{}, err + } + hostedZones := response.HostedZones + // TODO: Handle result truncation + // https://docs.aws.amazon.com/sdk-for-go/api/service/route53/Route53.html#ListHostedZones-instance_method + zoneList := make([]dnsprovider.Zone, len(hostedZones)) + for i, zone := range hostedZones { + zoneList[i] = &Zone{zone, &zones} + } + return zoneList, nil +}