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

Adding ec2tag and ec2region functions #24

Merged
merged 1 commit into from
Mar 28, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions aws/ec2info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package aws

import (
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
)

// Ec2Info -
type Ec2Info struct {
describer InstanceDescriber
metaClient *Ec2Meta
}

// InstanceDescriber - A subset of ec2iface.EC2API that we can use to call EC2.DescribeInstances
type InstanceDescriber interface {
DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
}

// var _ InstanceDescriber = (*ec2.EC2)(nil)

// NewEc2Info -
func NewEc2Info() *Ec2Info {
metaClient := &Ec2Meta{}
region := metaClient.Region()
return &Ec2Info{
describer: ec2Client(region),
metaClient: metaClient,
}
}

func ec2Client(region string) (client InstanceDescriber) {
config := aws.NewConfig()
config = config.WithRegion(region)
client = ec2.New(session.New(config))
return client
}

// Tag -
func (e *Ec2Info) Tag(tag string, def ...string) string {
instanceID := e.metaClient.Meta("instance-id")
input := &ec2.DescribeInstancesInput{
InstanceIds: aws.StringSlice([]string{instanceID}),
}
output, err := e.describer.DescribeInstances(input)
if err != nil {
log.Fatalf("Failed to list tag %s: %v", tag, err)
}
if output != nil && len(output.Reservations) > 0 &&
len(output.Reservations[0].Instances) > 0 &&
len(output.Reservations[0].Instances[0].Tags) > 0 {
for _, v := range output.Reservations[0].Instances[0].Tags {
if *v.Key == tag {
return *v.Value
}
}
}

if len(def) > 0 {
return def[0]
}
return ""
}
77 changes: 77 additions & 0 deletions aws/ec2info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package aws

import (
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
)

func TestTag_MissingKey(t *testing.T) {
server, ec2meta := MockServer(200, `"i-1234"`)
defer server.Close()
client := &DummyInstanceDescriber{
tags: []*ec2.Tag{
&ec2.Tag{
Key: aws.String("foo"),
Value: aws.String("bar"),
},
&ec2.Tag{
Key: aws.String("baz"),
Value: aws.String("qux"),
},
},
}
e := &Ec2Info{
describer: client,
metaClient: ec2meta,
}

assert.Empty(t, e.Tag("missing"))
assert.Equal(t, "default", e.Tag("missing", "default"))
}

func TestTag_ValidKey(t *testing.T) {
server, ec2meta := MockServer(200, `"i-1234"`)
defer server.Close()
client := &DummyInstanceDescriber{
tags: []*ec2.Tag{
&ec2.Tag{
Key: aws.String("foo"),
Value: aws.String("bar"),
},
&ec2.Tag{
Key: aws.String("baz"),
Value: aws.String("qux"),
},
},
}
e := &Ec2Info{
describer: client,
metaClient: ec2meta,
}

assert.Equal(t, "bar", e.Tag("foo"))
assert.Equal(t, "bar", e.Tag("foo", "default"))
}

// test doubles
type DummyInstanceDescriber struct {
tags []*ec2.Tag
}

func (d *DummyInstanceDescriber) DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
output := &ec2.DescribeInstancesOutput{
Reservations: []*ec2.Reservation{
&ec2.Reservation{
Instances: []*ec2.Instance{
&ec2.Instance{
Tags: d.tags,
},
},
},
},
}
return output, nil
}
34 changes: 30 additions & 4 deletions aws/ec2meta.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aws

import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
Expand Down Expand Up @@ -41,8 +42,8 @@ func (e *Ec2Meta) retrieveMetadata(url string, key string, def ...string) string
return value
}

// Ec2meta -
func (e *Ec2Meta) Ec2meta(key string, def ...string) string {
// Meta -
func (e *Ec2Meta) Meta(key string, def ...string) string {
if e.Endpoint == "" {
e.Endpoint = DefaultEndpoint
}
Expand All @@ -51,12 +52,37 @@ func (e *Ec2Meta) Ec2meta(key string, def ...string) string {
return e.retrieveMetadata(url, key, def...)
}

// Ec2dynamic -
func (e *Ec2Meta) Ec2dynamic(key string, def ...string) string {
// Dynamic -
func (e *Ec2Meta) Dynamic(key string, def ...string) string {
if e.Endpoint == "" {
e.Endpoint = DefaultEndpoint
}

url := e.Endpoint + "/latest/dynamic/" + key
return e.retrieveMetadata(url, key, def...)
}

// Region -
func (e *Ec2Meta) Region() string {
doc := e.Dynamic("instance-identity/document", `{"region":"unknown"}`)
obj := &InstanceDocument{
Region: "unknown",
}
err := json.Unmarshal([]byte(doc), &obj)
if err != nil {
log.Fatalf("Unable to unmarshal JSON object %s: %v", doc, err)
}
return obj.Region
}

// InstanceDocument -
type InstanceDocument struct {
PrivateIP string `json:"privateIp"`
AvailabilityZone string `json:"availabilityZone"`
InstanceID string `json:"InstanceId"`
InstanceType string `json:"InstanceType"`
AccountID string `json:"AccountId"`
ImageID string `json:"imageId"`
Architecture string `json:"architecture"`
Region string `json:"region"`
}
38 changes: 26 additions & 12 deletions aws/ec2meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,48 @@ import (
"github.com/stretchr/testify/assert"
)

func TestEc2meta_MissingKey(t *testing.T) {
func TestMeta_MissingKey(t *testing.T) {
server, ec2meta := MockServer(404, "")
defer server.Close()

assert.Empty(t, ec2meta.Ec2meta("foo"))
assert.Equal(t, "default", ec2meta.Ec2meta("foo", "default"))
assert.Empty(t, ec2meta.Meta("foo"))
assert.Equal(t, "default", ec2meta.Meta("foo", "default"))
}

func TestEc2meta_ValidKey(t *testing.T) {
func TestMeta_ValidKey(t *testing.T) {
server, ec2meta := MockServer(200, "i-1234")
defer server.Close()

assert.Equal(t, "i-1234", ec2meta.Ec2meta("instance-id"))
assert.Equal(t, "i-1234", ec2meta.Ec2meta("instance-id", "unused default"))
assert.Equal(t, "i-1234", ec2meta.Meta("instance-id"))
assert.Equal(t, "i-1234", ec2meta.Meta("instance-id", "unused default"))
}

func TestEc2dynamic_MissingKey(t *testing.T) {
func TestDynamic_MissingKey(t *testing.T) {
server, ec2meta := MockServer(404, "")
defer server.Close()

assert.Empty(t, ec2meta.Ec2dynamic("foo"))
assert.Equal(t, "default", ec2meta.Ec2dynamic("foo", "default"))
assert.Empty(t, ec2meta.Dynamic("foo"))
assert.Equal(t, "default", ec2meta.Dynamic("foo", "default"))
}

func TestEc2dynamic_ValidKey(t *testing.T) {
func TestDynamic_ValidKey(t *testing.T) {
server, ec2meta := MockServer(200, "i-1234")
defer server.Close()

assert.Equal(t, "i-1234", ec2meta.Ec2dynamic("instance-id"))
assert.Equal(t, "i-1234", ec2meta.Ec2dynamic("instance-id", "unused default"))
assert.Equal(t, "i-1234", ec2meta.Dynamic("instance-id"))
assert.Equal(t, "i-1234", ec2meta.Dynamic("instance-id", "unused default"))
}

func TestRegion_NoRegion(t *testing.T) {
server, ec2meta := MockServer(200, "{}")
defer server.Close()

assert.Equal(t, "unknown", ec2meta.Region())
}

func TestRegion_KnownRegion(t *testing.T) {
server, ec2meta := MockServer(200, `{"region":"us-east-1"}`)
defer server.Close()

assert.Equal(t, "us-east-1", ec2meta.Region())
}
12 changes: 5 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,16 @@ func NewGomplate() *Gomplate {
env := &Env{}
typeconv := &TypeConv{}
ec2meta := &aws.Ec2Meta{}
ec2info := aws.NewEc2Info()
return &Gomplate{
funcMap: template.FuncMap{
"Getenv": env.Getenv,
"getenv": env.Getenv,
"Bool": typeconv.Bool,
"bool": typeconv.Bool,
"JSON": typeconv.JSON,
"json": typeconv.JSON,
"Ec2meta": ec2meta.Ec2meta,
"ec2meta": ec2meta.Ec2meta,
"Ec2dynamic": ec2meta.Ec2dynamic,
"ec2dynamic": ec2meta.Ec2dynamic,
"ec2meta": ec2meta.Meta,
"ec2dynamic": ec2meta.Dynamic,
"ec2tag": ec2info.Tag,
"region": ec2meta.Region,
},
}
}
Expand Down
26 changes: 20 additions & 6 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,28 @@ func testTemplate(g *Gomplate, template string) string {
}

func TestGetenvTemplates(t *testing.T) {
g := NewGomplate()
env := &Env{}
typeconv := &TypeConv{}
g := &Gomplate{
funcMap: template.FuncMap{
"getenv": env.Getenv,
"bool": typeconv.Bool,
},
}
assert.Empty(t, testTemplate(g, `{{getenv "BLAHBLAHBLAH"}}`))
assert.Equal(t, os.Getenv("USER"), testTemplate(g, `{{getenv "USER"}}`))
assert.Equal(t, "default value", testTemplate(g, `{{getenv "BLAHBLAHBLAH" "default value"}}`))
}

func TestBoolTemplates(t *testing.T) {
g := NewGomplate()
env := &Env{}
typeconv := &TypeConv{}
g := &Gomplate{
funcMap: template.FuncMap{
"getenv": env.Getenv,
"bool": typeconv.Bool,
},
}
assert.Equal(t, "true", testTemplate(g, `{{bool "true"}}`))
assert.Equal(t, "false", testTemplate(g, `{{bool "false"}}`))
assert.Equal(t, "false", testTemplate(g, `{{bool "foo"}}`))
Expand All @@ -39,7 +53,7 @@ func TestEc2MetaTemplates_MissingKey(t *testing.T) {
defer server.Close()
g := &Gomplate{
funcMap: template.FuncMap{
"ec2meta": ec2meta.Ec2meta,
"ec2meta": ec2meta.Meta,
},
}

Expand All @@ -52,7 +66,7 @@ func TestEc2MetaTemplates_ValidKey(t *testing.T) {
defer server.Close()
g := &Gomplate{
funcMap: template.FuncMap{
"ec2meta": ec2meta.Ec2meta,
"ec2meta": ec2meta.Meta,
},
}

Expand All @@ -66,8 +80,8 @@ func TestEc2MetaTemplates_WithJSON(t *testing.T) {
ty := new(TypeConv)
g := &Gomplate{
funcMap: template.FuncMap{
"ec2meta": ec2meta.Ec2meta,
"ec2dynamic": ec2meta.Ec2dynamic,
"ec2meta": ec2meta.Meta,
"ec2dynamic": ec2meta.Dynamic,
"json": ty.JSON,
},
}
Expand Down