1package ec2metadata
2
3import (
4	"encoding/json"
5	"fmt"
6	"path"
7	"strings"
8	"time"
9
10	"github.com/aws/aws-sdk-go/aws/awserr"
11	"github.com/aws/aws-sdk-go/aws/request"
12)
13
14// GetMetadata uses the path provided to request information from the EC2
15// instance metdata service. The content will be returned as a string, or
16// error if the request failed.
17func (c *EC2Metadata) GetMetadata(p string) (string, error) {
18	op := &request.Operation{
19		Name:       "GetMetadata",
20		HTTPMethod: "GET",
21		HTTPPath:   path.Join("/", "meta-data", p),
22	}
23
24	output := &metadataOutput{}
25	req := c.NewRequest(op, nil, output)
26
27	return output.Content, req.Send()
28}
29
30// GetDynamicData uses the path provided to request information from the EC2
31// instance metadata service for dynamic data. The content will be returned
32// as a string, or error if the request failed.
33func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
34	op := &request.Operation{
35		Name:       "GetDynamicData",
36		HTTPMethod: "GET",
37		HTTPPath:   path.Join("/", "dynamic", p),
38	}
39
40	output := &metadataOutput{}
41	req := c.NewRequest(op, nil, output)
42
43	return output.Content, req.Send()
44}
45
46// GetInstanceIdentityDocument retrieves an identity document describing an
47// instance. Error is returned if the request fails or is unable to parse
48// the response.
49func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
50	resp, err := c.GetDynamicData("instance-identity/document")
51	if err != nil {
52		return EC2InstanceIdentityDocument{},
53			awserr.New("EC2MetadataRequestError",
54				"failed to get EC2 instance identity document", err)
55	}
56
57	doc := EC2InstanceIdentityDocument{}
58	if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil {
59		return EC2InstanceIdentityDocument{},
60			awserr.New("SerializationError",
61				"failed to decode EC2 instance identity document", err)
62	}
63
64	return doc, nil
65}
66
67// IAMInfo retrieves IAM info from the metadata API
68func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
69	resp, err := c.GetMetadata("iam/info")
70	if err != nil {
71		return EC2IAMInfo{},
72			awserr.New("EC2MetadataRequestError",
73				"failed to get EC2 IAM info", err)
74	}
75
76	info := EC2IAMInfo{}
77	if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil {
78		return EC2IAMInfo{},
79			awserr.New("SerializationError",
80				"failed to decode EC2 IAM info", err)
81	}
82
83	if info.Code != "Success" {
84		errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code)
85		return EC2IAMInfo{},
86			awserr.New("EC2MetadataError", errMsg, nil)
87	}
88
89	return info, nil
90}
91
92// Region returns the region the instance is running in.
93func (c *EC2Metadata) Region() (string, error) {
94	resp, err := c.GetMetadata("placement/availability-zone")
95	if err != nil {
96		return "", err
97	}
98
99	// returns region without the suffix. Eg: us-west-2a becomes us-west-2
100	return resp[:len(resp)-1], nil
101}
102
103// Available returns if the application has access to the EC2 Metadata service.
104// Can be used to determine if application is running within an EC2 Instance and
105// the metadata service is available.
106func (c *EC2Metadata) Available() bool {
107	if _, err := c.GetMetadata("instance-id"); err != nil {
108		return false
109	}
110
111	return true
112}
113
114// An EC2IAMInfo provides the shape for unmarshalling
115// an IAM info from the metadata API
116type EC2IAMInfo struct {
117	Code               string
118	LastUpdated        time.Time
119	InstanceProfileArn string
120	InstanceProfileID  string
121}
122
123// An EC2InstanceIdentityDocument provides the shape for unmarshalling
124// an instance identity document
125type EC2InstanceIdentityDocument struct {
126	DevpayProductCodes []string  `json:"devpayProductCodes"`
127	AvailabilityZone   string    `json:"availabilityZone"`
128	PrivateIP          string    `json:"privateIp"`
129	Version            string    `json:"version"`
130	Region             string    `json:"region"`
131	InstanceID         string    `json:"instanceId"`
132	BillingProducts    []string  `json:"billingProducts"`
133	InstanceType       string    `json:"instanceType"`
134	AccountID          string    `json:"accountId"`
135	PendingTime        time.Time `json:"pendingTime"`
136	ImageID            string    `json:"imageId"`
137	KernelID           string    `json:"kernelId"`
138	RamdiskID          string    `json:"ramdiskId"`
139	Architecture       string    `json:"architecture"`
140}
141