service.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Package ec2metadata provides the client for making API calls to the
  2. // EC2 Metadata service.
  3. //
  4. // This package's client can be disabled completely by setting the environment
  5. // variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
  6. // true instructs the SDK to disable the EC2 Metadata client. The client cannot
  7. // be used while the environemnt variable is set to true, (case insensitive).
  8. package ec2metadata
  9. import (
  10. "bytes"
  11. "errors"
  12. "io"
  13. "net/http"
  14. "os"
  15. "strings"
  16. "time"
  17. "github.com/aws/aws-sdk-go/aws"
  18. "github.com/aws/aws-sdk-go/aws/awserr"
  19. "github.com/aws/aws-sdk-go/aws/client"
  20. "github.com/aws/aws-sdk-go/aws/client/metadata"
  21. "github.com/aws/aws-sdk-go/aws/corehandlers"
  22. "github.com/aws/aws-sdk-go/aws/request"
  23. )
  24. // ServiceName is the name of the service.
  25. const ServiceName = "ec2metadata"
  26. const disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED"
  27. // A EC2Metadata is an EC2 Metadata service Client.
  28. type EC2Metadata struct {
  29. *client.Client
  30. }
  31. // New creates a new instance of the EC2Metadata client with a session.
  32. // This client is safe to use across multiple goroutines.
  33. //
  34. //
  35. // Example:
  36. // // Create a EC2Metadata client from just a session.
  37. // svc := ec2metadata.New(mySession)
  38. //
  39. // // Create a EC2Metadata client with additional configuration
  40. // svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
  41. func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
  42. c := p.ClientConfig(ServiceName, cfgs...)
  43. return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
  44. }
  45. // NewClient returns a new EC2Metadata client. Should be used to create
  46. // a client when not using a session. Generally using just New with a session
  47. // is preferred.
  48. //
  49. // If an unmodified HTTP client is provided from the stdlib default, or no client
  50. // the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
  51. // To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
  52. func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
  53. if !aws.BoolValue(cfg.EC2MetadataDisableTimeoutOverride) && httpClientZero(cfg.HTTPClient) {
  54. // If the http client is unmodified and this feature is not disabled
  55. // set custom timeouts for EC2Metadata requests.
  56. cfg.HTTPClient = &http.Client{
  57. // use a shorter timeout than default because the metadata
  58. // service is local if it is running, and to fail faster
  59. // if not running on an ec2 instance.
  60. Timeout: 5 * time.Second,
  61. }
  62. }
  63. svc := &EC2Metadata{
  64. Client: client.New(
  65. cfg,
  66. metadata.ClientInfo{
  67. ServiceName: ServiceName,
  68. Endpoint: endpoint,
  69. APIVersion: "latest",
  70. },
  71. handlers,
  72. ),
  73. }
  74. svc.Handlers.Unmarshal.PushBack(unmarshalHandler)
  75. svc.Handlers.UnmarshalError.PushBack(unmarshalError)
  76. svc.Handlers.Validate.Clear()
  77. svc.Handlers.Validate.PushBack(validateEndpointHandler)
  78. // Disable the EC2 Metadata service if the environment variable is set.
  79. // This shortcirctes the service's functionality to always fail to send
  80. // requests.
  81. if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" {
  82. svc.Handlers.Send.SwapNamed(request.NamedHandler{
  83. Name: corehandlers.SendHandler.Name,
  84. Fn: func(r *request.Request) {
  85. r.Error = awserr.New(
  86. request.CanceledErrorCode,
  87. "EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
  88. nil)
  89. },
  90. })
  91. }
  92. // Add additional options to the service config
  93. for _, option := range opts {
  94. option(svc.Client)
  95. }
  96. return svc
  97. }
  98. func httpClientZero(c *http.Client) bool {
  99. return c == nil || (c.Transport == nil && c.CheckRedirect == nil && c.Jar == nil && c.Timeout == 0)
  100. }
  101. type metadataOutput struct {
  102. Content string
  103. }
  104. func unmarshalHandler(r *request.Request) {
  105. defer r.HTTPResponse.Body.Close()
  106. b := &bytes.Buffer{}
  107. if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
  108. r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err)
  109. return
  110. }
  111. if data, ok := r.Data.(*metadataOutput); ok {
  112. data.Content = b.String()
  113. }
  114. }
  115. func unmarshalError(r *request.Request) {
  116. defer r.HTTPResponse.Body.Close()
  117. b := &bytes.Buffer{}
  118. if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
  119. r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err)
  120. return
  121. }
  122. // Response body format is not consistent between metadata endpoints.
  123. // Grab the error message as a string and include that as the source error
  124. r.Error = awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String()))
  125. }
  126. func validateEndpointHandler(r *request.Request) {
  127. if r.ClientInfo.Endpoint == "" {
  128. r.Error = aws.ErrMissingEndpoint
  129. }
  130. }