| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // Package ec2metadata provides the client for making API calls to the
- // EC2 Metadata service.
- //
- // This package's client can be disabled completely by setting the environment
- // variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
- // true instructs the SDK to disable the EC2 Metadata client. The client cannot
- // be used while the environemnt variable is set to true, (case insensitive).
- package ec2metadata
- import (
- "bytes"
- "errors"
- "io"
- "net/http"
- "os"
- "strings"
- "time"
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/awserr"
- "github.com/aws/aws-sdk-go/aws/client"
- "github.com/aws/aws-sdk-go/aws/client/metadata"
- "github.com/aws/aws-sdk-go/aws/corehandlers"
- "github.com/aws/aws-sdk-go/aws/request"
- )
- // ServiceName is the name of the service.
- const ServiceName = "ec2metadata"
- const disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED"
- // A EC2Metadata is an EC2 Metadata service Client.
- type EC2Metadata struct {
- *client.Client
- }
- // New creates a new instance of the EC2Metadata client with a session.
- // This client is safe to use across multiple goroutines.
- //
- //
- // Example:
- // // Create a EC2Metadata client from just a session.
- // svc := ec2metadata.New(mySession)
- //
- // // Create a EC2Metadata client with additional configuration
- // svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
- func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
- c := p.ClientConfig(ServiceName, cfgs...)
- return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
- }
- // NewClient returns a new EC2Metadata client. Should be used to create
- // a client when not using a session. Generally using just New with a session
- // is preferred.
- //
- // If an unmodified HTTP client is provided from the stdlib default, or no client
- // the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
- // To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
- func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
- if !aws.BoolValue(cfg.EC2MetadataDisableTimeoutOverride) && httpClientZero(cfg.HTTPClient) {
- // If the http client is unmodified and this feature is not disabled
- // set custom timeouts for EC2Metadata requests.
- cfg.HTTPClient = &http.Client{
- // use a shorter timeout than default because the metadata
- // service is local if it is running, and to fail faster
- // if not running on an ec2 instance.
- Timeout: 5 * time.Second,
- }
- }
- svc := &EC2Metadata{
- Client: client.New(
- cfg,
- metadata.ClientInfo{
- ServiceName: ServiceName,
- Endpoint: endpoint,
- APIVersion: "latest",
- },
- handlers,
- ),
- }
- svc.Handlers.Unmarshal.PushBack(unmarshalHandler)
- svc.Handlers.UnmarshalError.PushBack(unmarshalError)
- svc.Handlers.Validate.Clear()
- svc.Handlers.Validate.PushBack(validateEndpointHandler)
- // Disable the EC2 Metadata service if the environment variable is set.
- // This shortcirctes the service's functionality to always fail to send
- // requests.
- if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" {
- svc.Handlers.Send.SwapNamed(request.NamedHandler{
- Name: corehandlers.SendHandler.Name,
- Fn: func(r *request.Request) {
- r.Error = awserr.New(
- request.CanceledErrorCode,
- "EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
- nil)
- },
- })
- }
- // Add additional options to the service config
- for _, option := range opts {
- option(svc.Client)
- }
- return svc
- }
- func httpClientZero(c *http.Client) bool {
- return c == nil || (c.Transport == nil && c.CheckRedirect == nil && c.Jar == nil && c.Timeout == 0)
- }
- type metadataOutput struct {
- Content string
- }
- func unmarshalHandler(r *request.Request) {
- defer r.HTTPResponse.Body.Close()
- b := &bytes.Buffer{}
- if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
- r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err)
- return
- }
- if data, ok := r.Data.(*metadataOutput); ok {
- data.Content = b.String()
- }
- }
- func unmarshalError(r *request.Request) {
- defer r.HTTPResponse.Body.Close()
- b := &bytes.Buffer{}
- if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
- r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err)
- return
- }
- // Response body format is not consistent between metadata endpoints.
- // Grab the error message as a string and include that as the source error
- r.Error = awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String()))
- }
- func validateEndpointHandler(r *request.Request) {
- if r.ClientInfo.Endpoint == "" {
- r.Error = aws.ErrMissingEndpoint
- }
- }
|