mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
AWS Route53 dnsprovider
This commit is contained in:
parent
9dc06e85fb
commit
7c14d767c5
@ -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
|
||||
}
|
44
federation/pkg/dnsprovider/providers/aws/route53/route53.go
Normal file
44
federation/pkg/dnsprovider/providers/aws/route53/route53.go
Normal file
@ -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
|
||||
}
|
236
federation/pkg/dnsprovider/providers/aws/route53/route53_test.go
Normal file
236
federation/pkg/dnsprovider/providers/aws/route53/route53_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
53
federation/pkg/dnsprovider/providers/aws/route53/rrset.go
Normal file
53
federation/pkg/dnsprovider/providers/aws/route53/rrset.go
Normal file
@ -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)
|
||||
}
|
135
federation/pkg/dnsprovider/providers/aws/route53/rrsets.go
Normal file
135
federation/pkg/dnsprovider/providers/aws/route53/rrsets.go
Normal file
@ -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,
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
38
federation/pkg/dnsprovider/providers/aws/route53/zone.go
Normal file
38
federation/pkg/dnsprovider/providers/aws/route53/zone.go
Normal file
@ -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
|
||||
}
|
45
federation/pkg/dnsprovider/providers/aws/route53/zones.go
Normal file
45
federation/pkg/dnsprovider/providers/aws/route53/zones.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user