Skip to content

Commit

Permalink
aws/ec2metadata: Add support for EC2 User Data (#872)
Browse files Browse the repository at this point in the history
Adds the `GetUserData` method to `EC2Metadata` client. This allows you
to get instance user data from the `/latest/user-data` EC2 metadata
endpoint.

If the instance does not have any data set the error code
`NotFoundError` will be returned. If any data is set that value as a
string will be returned by the method.

Fix #852
  • Loading branch information
jasdel committed Oct 5, 2016
1 parent 7eb100e commit 5717392
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
22 changes: 22 additions & 0 deletions aws/ec2metadata/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ec2metadata
import (
"encoding/json"
"fmt"
"net/http"
"path"
"strings"
"time"
Expand All @@ -27,6 +28,27 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
return output.Content, req.Send()
}

// GetUserData returns the userdata that was configured for the service. If
// there is no user-data setup for the EC2 instance a "NotFoundError" error
// code will be returned.
func (c *EC2Metadata) GetUserData() (string, error) {
op := &request.Operation{
Name: "GetUserData",
HTTPMethod: "GET",
HTTPPath: path.Join("/", "user-data"),
}

output := &metadataOutput{}
req := c.NewRequest(op, nil, output)
req.Handlers.UnmarshalError.PushBack(func(r *request.Request) {
if r.HTTPResponse.StatusCode == http.StatusNotFound {
r.Error = awserr.New("NotFoundError", "user-data not found", r.Error)
}
})

return output.Content, req.Send()
}

// GetDynamicData uses the path provided to request information from the EC2
// instance metadata service for dynamic data. The content will be returned
// as a string, or error if the request failed.
Expand Down
47 changes: 47 additions & 0 deletions aws/ec2metadata/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package ec2metadata_test

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -87,6 +89,51 @@ func TestGetMetadata(t *testing.T) {
assert.Equal(t, "success", resp)
}

func TestGetUserData(t *testing.T) {
server := initTestServer(
"/latest/user-data",
"success", // real response includes suffix
)
defer server.Close()
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})

resp, err := c.GetUserData()

assert.NoError(t, err)
assert.Equal(t, "success", resp)
}

func TestGetUserData_Error(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reader := strings.NewReader(`<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>404 - Not Found</title>
</head>
<body>
<h1>404 - Not Found</h1>
</body>
</html>`)
w.Header().Set("Content-Type", "text/html")
w.Header().Set("Content-Length", fmt.Sprintf("%d", reader.Len()))
w.WriteHeader(http.StatusNotFound)
io.Copy(w, reader)
}))

defer server.Close()
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})

resp, err := c.GetUserData()
assert.Error(t, err)
assert.Empty(t, resp)

aerr, ok := err.(awserr.Error)
assert.True(t, ok)
assert.Equal(t, "NotFoundError", aerr.Code())
}

func TestGetRegion(t *testing.T) {
server := initTestServer(
"/latest/meta-data/placement/availability-zone",
Expand Down

0 comments on commit 5717392

Please sign in to comment.