api.go 4.9 KB

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