Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS support for Zones #5230

Merged
merged 1 commit into from
Mar 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 50 additions & 6 deletions pkg/cloudprovider/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,22 @@ import (
type EC2 interface {
// Query EC2 for instances matching the filter
Instances(instIds []string, filter *ec2InstanceFilter) (resp *ec2.InstancesResp, err error)

// Query the EC2 metadata service (used to discover instance-id etc)
GetMetaData(key string) ([]byte, error)
}

// AWSCloud is an implementation of Interface, TCPLoadBalancer and Instances for Amazon Web Services.
type AWSCloud struct {
ec2 EC2
cfg *AWSCloudConfig
ec2 EC2
cfg *AWSCloudConfig
availabilityZone string
region aws.Region
}

type AWSCloudConfig struct {
Global struct {
// TODO: Is there any use for this? We can get it from the instance metadata service
Region string
}
}
Expand Down Expand Up @@ -82,6 +88,14 @@ func (self *GoamzEC2) Instances(instanceIds []string, filter *ec2InstanceFilter)
return self.ec2.Instances(instanceIds, goamzFilter)
}

func (self *GoamzEC2) GetMetaData(key string) ([]byte, error) {
v, err := aws.GetMetaData(key)
if err != nil {
return nil, fmt.Errorf("Error querying AWS metadata for key %s: %v", key, err)
}
return v, nil
}

type AuthFunc func() (auth aws.Auth, err error)

func init() {
Expand Down Expand Up @@ -125,18 +139,36 @@ func newAWSCloud(config io.Reader, authFunc AuthFunc) (*AWSCloud, error) {
return nil, err
}

// TODO: We can get the region very easily from the instance-metadata service
region, ok := aws.Regions[cfg.Global.Region]
if !ok {
return nil, fmt.Errorf("not a valid AWS region: %s", cfg.Global.Region)
}

ec2 := ec2.New(auth, region)
return &AWSCloud{
ec2: &GoamzEC2{ec2: ec2},
cfg: cfg,
ec2: &GoamzEC2{ec2: ec2.New(auth, region)},
cfg: cfg,
region: region,
}, nil
}

func (self *AWSCloud) getAvailabilityZone() (string, error) {
// TODO: Do we need sync.Mutex here?
availabilityZone := self.availabilityZone
if self.availabilityZone == "" {
availabilityZoneBytes, err := self.ec2.GetMetaData("placement/availability-zone")
if err != nil {
return "", err
}
if availabilityZoneBytes == nil || len(availabilityZoneBytes) == 0 {
return "", fmt.Errorf("Unable to determine availability-zone from instance metadata")
}
availabilityZone = string(availabilityZoneBytes)
self.availabilityZone = availabilityZone
}
return availabilityZone, nil
}

func (aws *AWSCloud) Clusters() (cloudprovider.Clusters, bool) {
return nil, false
}
Expand All @@ -153,7 +185,7 @@ func (aws *AWSCloud) Instances() (cloudprovider.Instances, bool) {

// Zones returns an implementation of Zones for Amazon Web Services.
func (aws *AWSCloud) Zones() (cloudprovider.Zones, bool) {
return nil, false
return aws, true
}

// IPAddress is an implementation of Instances.IPAddress.
Expand Down Expand Up @@ -246,3 +278,15 @@ func (aws *AWSCloud) List(filter string) ([]string, error) {
func (v *AWSCloud) GetNodeResources(name string) (*api.NodeResources, error) {
return nil, nil
}

// GetZone implements Zones.GetZone
func (self *AWSCloud) GetZone() (cloudprovider.Zone, error) {
availabilityZone, err := self.getAvailabilityZone()
if err != nil {
return cloudprovider.Zone{}, err
}
return cloudprovider.Zone{
FailureDomain: availabilityZone,
Region: self.region.Name,
}, nil
}
69 changes: 53 additions & 16 deletions pkg/cloudprovider/aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,47 @@ func TestNewAWSCloud(t *testing.T) {
}

type FakeEC2 struct {
instances func(instanceIds []string, filter *ec2InstanceFilter) (resp *ec2.InstancesResp, err error)
instances []ec2.Instance
availabilityZone string
}

func (ec2 *FakeEC2) Instances(instanceIds []string, filter *ec2InstanceFilter) (resp *ec2.InstancesResp, err error) {
return ec2.instances(instanceIds, filter)
func (self *FakeEC2) Instances(instanceIds []string, filter *ec2InstanceFilter) (resp *ec2.InstancesResp, err error) {
matches := []ec2.Instance{}
for _, instance := range self.instances {
if filter == nil || filter.Matches(instance) {
matches = append(matches, instance)
}
}
return &ec2.InstancesResp{"",
[]ec2.Reservation{
{"", "", "", nil, matches}}}, nil
}

func (self *FakeEC2) GetMetaData(key string) ([]byte, error) {
if key == "placement/availability-zone" {
return []byte(self.availabilityZone), nil
} else {
return nil, nil
}
}

func mockInstancesResp(instances []ec2.Instance) (aws *AWSCloud) {
availabilityZone := "us-west-2d"
return &AWSCloud{
ec2: &FakeEC2{
instances: instances,
availabilityZone: availabilityZone,
},
}
}

func mockAvailabilityZone(region string, availabilityZone string) *AWSCloud {
return &AWSCloud{
&FakeEC2{
func(instanceIds []string, filter *ec2InstanceFilter) (resp *ec2.InstancesResp, err error) {
matches := []ec2.Instance{}
for _, instance := range instances {
if filter == nil || filter.Matches(instance) {
matches = append(matches, instance)
}
}
return &ec2.InstancesResp{"",
[]ec2.Reservation{
{"", "", "", nil, matches}}}, nil
}},
nil}
ec2: &FakeEC2{
availabilityZone: availabilityZone,
},
region: aws.Regions[region],
}
}

func TestList(t *testing.T) {
Expand Down Expand Up @@ -163,3 +182,21 @@ func TestIPAddress(t *testing.T) {
t.Errorf("Expected %v, got %v", e, a)
}
}

func TestGetRegion(t *testing.T) {
aws := mockAvailabilityZone("us-west-2", "us-west-2e")
zones, ok := aws.Zones()
if !ok {
t.Fatalf("Unexpected missing zones impl")
}
zone, err := zones.GetZone()
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if zone.Region != "us-west-2" {
t.Errorf("Unexpected region: %s", zone.Region)
}
if zone.FailureDomain != "us-west-2e" {
t.Errorf("Unexpected FailureDomain: %s", zone.FailureDomain)
}
}