Use AWS SDK EC2 metadata client.

This commit is contained in:
Trevor Pounds 2015-09-20 13:09:08 -07:00
parent df0718caa1
commit 9cd91d111d
2 changed files with 41 additions and 63 deletions

View File

@ -20,7 +20,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -34,6 +33,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/elb"
@ -61,7 +61,7 @@ type AWSServices interface {
Compute(region string) (EC2, error) Compute(region string) (EC2, error)
LoadBalancing(region string) (ELB, error) LoadBalancing(region string) (ELB, error)
Autoscaling(region string) (ASG, error) Autoscaling(region string) (ASG, error)
Metadata() AWSMetadata Metadata() (EC2Metadata, error)
} }
// TODO: Should we rename this to AWS (EBS & ELB are not technically part of EC2) // TODO: Should we rename this to AWS (EBS & ELB are not technically part of EC2)
@ -129,9 +129,9 @@ type ASG interface {
} }
// Abstraction over the AWS metadata service // Abstraction over the AWS metadata service
type AWSMetadata interface { type EC2Metadata interface {
// Query the EC2 metadata service (used to discover instance-id etc) // Query the EC2 metadata service (used to discover instance-id etc)
GetMetaData(key string) ([]byte, error) GetMetadata(path string) (string, error)
} }
type VolumeOptions struct { type VolumeOptions struct {
@ -175,6 +175,7 @@ type AWSCloud struct {
ec2 EC2 ec2 EC2
elb ELB elb ELB
asg ASG asg ASG
metadata EC2Metadata
cfg *AWSCloudConfig cfg *AWSCloudConfig
availabilityZone string availabilityZone string
region string region string
@ -232,8 +233,9 @@ func (p *awsSDKProvider) Autoscaling(regionName string) (ASG, error) {
return client, nil return client, nil
} }
func (p *awsSDKProvider) Metadata() AWSMetadata { func (p *awsSDKProvider) Metadata() (EC2Metadata, error) {
return &awsSdkMetadata{} client := ec2metadata.New(nil)
return client, nil
} }
func stringPointerArray(orig []string) []*string { func stringPointerArray(orig []string) []*string {
@ -307,34 +309,16 @@ func (self *awsSdkEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([
} }
type awsSdkMetadata struct { type awsSdkMetadata struct {
metadata *ec2metadata.Client
} }
var metadataClient = http.Client{ var metadataClient = http.Client{
Timeout: time.Second * 10, Timeout: time.Second * 10,
} }
// Implements AWSMetadata.GetMetaData // Implements EC2Metadata.GetMetadata
func (self *awsSdkMetadata) GetMetaData(key string) ([]byte, error) { func (self *awsSdkMetadata) GetMetadata(path string) (string, error) {
// TODO Get an implementation of this merged into aws-sdk-go return self.metadata.GetMetadata(path)
url := "http://169.254.169.254/latest/meta-data/" + key
res, err := metadataClient.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != 200 {
err = fmt.Errorf("Code %d returned for url %s", res.StatusCode, url)
return nil, fmt.Errorf("Error querying AWS metadata for key %s: %v", key, err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("Error querying AWS metadata for key %s: %v", key, err)
}
return []byte(body), nil
} }
// Implements EC2.DescribeSecurityGroups // Implements EC2.DescribeSecurityGroups
@ -466,7 +450,7 @@ func init() {
} }
// readAWSCloudConfig reads an instance of AWSCloudConfig from config reader. // readAWSCloudConfig reads an instance of AWSCloudConfig from config reader.
func readAWSCloudConfig(config io.Reader, metadata AWSMetadata) (*AWSCloudConfig, error) { func readAWSCloudConfig(config io.Reader, metadata EC2Metadata) (*AWSCloudConfig, error) {
var cfg AWSCloudConfig var cfg AWSCloudConfig
var err error var err error
@ -493,15 +477,8 @@ func readAWSCloudConfig(config io.Reader, metadata AWSMetadata) (*AWSCloudConfig
return &cfg, nil return &cfg, nil
} }
func getAvailabilityZone(metadata AWSMetadata) (string, error) { func getAvailabilityZone(metadata EC2Metadata) (string, error) {
availabilityZoneBytes, err := metadata.GetMetaData("placement/availability-zone") return metadata.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")
}
return string(availabilityZoneBytes), nil
} }
func isRegionValid(region string) bool { func isRegionValid(region string) bool {
@ -527,7 +504,11 @@ func isRegionValid(region string) bool {
// newAWSCloud creates a new instance of AWSCloud. // newAWSCloud creates a new instance of AWSCloud.
// AWSProvider and instanceId are primarily for tests // AWSProvider and instanceId are primarily for tests
func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) { func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) {
metadata := awsServices.Metadata() metadata, err := awsServices.Metadata()
if err != nil {
return nil, fmt.Errorf("error creating AWS metadata client: %v", err)
}
cfg, err := readAWSCloudConfig(config, metadata) cfg, err := readAWSCloudConfig(config, metadata)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read AWS cloud provider config file: %v", err) return nil, fmt.Errorf("unable to read AWS cloud provider config file: %v", err)
@ -564,6 +545,7 @@ func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) {
ec2: ec2, ec2: ec2,
elb: elb, elb: elb,
asg: asg, asg: asg,
metadata: metadata,
cfg: cfg, cfg: cfg,
region: regionName, region: regionName,
availabilityZone: zone, availabilityZone: zone,
@ -1059,17 +1041,16 @@ func (s *AWSCloud) getSelfAWSInstance() (*awsInstance, error) {
i := s.selfAWSInstance i := s.selfAWSInstance
if i == nil { if i == nil {
metadata := s.awsServices.Metadata() instanceId, err := s.metadata.GetMetadata("instance-id")
instanceIdBytes, err := metadata.GetMetaData("instance-id")
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching instance-id from ec2 metadata service: %v", err) return nil, fmt.Errorf("error fetching instance-id from ec2 metadata service: %v", err)
} }
privateDnsNameBytes, err := metadata.GetMetaData("local-hostname") privateDnsName, err := s.metadata.GetMetadata("local-hostname")
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching local-hostname from ec2 metadata service: %v", err) return nil, fmt.Errorf("error fetching local-hostname from ec2 metadata service: %v", err)
} }
i = newAWSInstance(s.ec2, string(instanceIdBytes), string(privateDnsNameBytes)) i = newAWSInstance(s.ec2, instanceId, privateDnsName)
s.selfAWSInstance = i s.selfAWSInstance = i
} }
@ -1254,27 +1235,24 @@ func (s *AWSCloud) describeLoadBalancer(name string) (*elb.LoadBalancerDescripti
// Retrieves instance's vpc id from metadata // Retrieves instance's vpc id from metadata
func (self *AWSCloud) findVPCID() (string, error) { func (self *AWSCloud) findVPCID() (string, error) {
macs, err := self.metadata.GetMetadata("network/interfaces/macs/")
metadata := self.awsServices.Metadata()
macsBytes, err := metadata.GetMetaData("network/interfaces/macs/")
if err != nil { if err != nil {
return "", fmt.Errorf("Could not list interfaces of the instance", err) return "", fmt.Errorf("Could not list interfaces of the instance", err)
} }
// loop over interfaces, first vpc id returned wins // loop over interfaces, first vpc id returned wins
for _, macPath := range strings.Split(string(macsBytes), "\n") { for _, macPath := range strings.Split(macs, "\n") {
if len(macPath) == 0 { if len(macPath) == 0 {
continue continue
} }
url := fmt.Sprintf("network/interfaces/macs/%svpc-id", macPath) url := fmt.Sprintf("network/interfaces/macs/%svpc-id", macPath)
vpcIDBytes, err := metadata.GetMetaData(url) vpcID, err := self.metadata.GetMetadata(url)
if err != nil { if err != nil {
continue continue
} }
return string(vpcIDBytes), nil return vpcID, nil
} }
return "", fmt.Errorf("Could not find VPC id in instance metadata") return "", fmt.Errorf("Could not find VPC ID in instance metadata")
} }
// Find the VPC which self is attached to. // Find the VPC which self is attached to.

View File

@ -83,9 +83,9 @@ func TestReadAWSCloudConfig(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Logf("Running test case %s", test.name) t.Logf("Running test case %s", test.name)
var metadata AWSMetadata var metadata EC2Metadata
if test.aws != nil { if test.aws != nil {
metadata = test.aws.Metadata() metadata, _ = test.aws.Metadata()
} }
cfg, err := readAWSCloudConfig(test.reader, metadata) cfg, err := readAWSCloudConfig(test.reader, metadata)
if test.expectError { if test.expectError {
@ -166,8 +166,8 @@ func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) {
return s.asg, nil return s.asg, nil
} }
func (s *FakeAWSServices) Metadata() AWSMetadata { func (s *FakeAWSServices) Metadata() (EC2Metadata, error) {
return s.metadata return s.metadata, nil
} }
func TestFilterTags(t *testing.T) { func TestFilterTags(t *testing.T) {
@ -313,31 +313,31 @@ type FakeMetadata struct {
aws *FakeAWSServices aws *FakeAWSServices
} }
func (self *FakeMetadata) GetMetaData(key string) ([]byte, error) { func (self *FakeMetadata) GetMetadata(key string) (string, error) {
networkInterfacesPrefix := "network/interfaces/macs/" networkInterfacesPrefix := "network/interfaces/macs/"
if key == "placement/availability-zone" { if key == "placement/availability-zone" {
return []byte(self.aws.availabilityZone), nil return self.aws.availabilityZone, nil
} else if key == "instance-id" { } else if key == "instance-id" {
return []byte(self.aws.instanceId), nil return self.aws.instanceId, nil
} else if key == "local-hostname" { } else if key == "local-hostname" {
return []byte(self.aws.privateDnsName), nil return self.aws.privateDnsName, nil
} else if strings.HasPrefix(key, networkInterfacesPrefix) { } else if strings.HasPrefix(key, networkInterfacesPrefix) {
if key == networkInterfacesPrefix { if key == networkInterfacesPrefix {
return []byte(strings.Join(self.aws.networkInterfacesMacs, "/\n") + "/\n"), nil return strings.Join(self.aws.networkInterfacesMacs, "/\n") + "/\n", nil
} else { } else {
keySplit := strings.Split(key, "/") keySplit := strings.Split(key, "/")
macParam := keySplit[3] macParam := keySplit[3]
if len(keySplit) == 5 && keySplit[4] == "vpc-id" { if len(keySplit) == 5 && keySplit[4] == "vpc-id" {
for i, macElem := range self.aws.networkInterfacesMacs { for i, macElem := range self.aws.networkInterfacesMacs {
if macParam == macElem { if macParam == macElem {
return []byte(self.aws.networkInterfacesVpcIDs[i]), nil return self.aws.networkInterfacesVpcIDs[i], nil
} }
} }
} }
return nil, nil return "", nil
} }
} else { } else {
return nil, nil return "", nil
} }
} }