class Aws::EC2Metadata

A client that can query version 2 of the EC2 Instance Metadata

def backoff(backoff)

def backoff(backoff)
  case backoff
  when Proc then backoff
  when Numeric then ->(_) { Kernel.sleep(backoff) }
  else ->(num_failures) { Kernel.sleep(1.2**num_failures) }
  end
end

def fetch_token

def fetch_token
  open_connection do |conn|
    created_time = Time.now
    token_value, token_ttl = http_put(conn, @token_ttl)
    @token = Token.new(value: token_value, ttl: token_ttl, created_time: created_time)
  end
end

def get(path)

Parameters:
  • path (String) -- The full path to the metadata.

Other tags:
    See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html -
    See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html -

Other tags:
    Note: - Unlike other services, IMDS does not have a service API model. This
    Note: - This implementation always returns a String and will not parse any

Other tags:
    Example: Fetching and parsing directory listings -
    Example: Fetching and parsing JSON meta-data -
    Example: Fetching the instance ID -
def get(path)
  retry_errors(max_retries: @retries) do
    @mutex.synchronize do
      fetch_token unless @token && !@token.expired?
    end
    open_connection do |conn|
      http_get(conn, path, @token.value)
    end
  end
end

def http_get(connection, path, token)

def http_get(connection, path, token)
  headers = {
    'User-Agent' => "aws-sdk-ruby3/#{CORE_GEM_VERSION}",
    'x-aws-ec2-metadata-token' => token
  }
  request = Net::HTTP::Get.new(path, headers)
  response = connection.request(request)
  case response.code.to_i
  when 200
    response.body
  when 401
    raise TokenExpiredError
  when 404
    raise MetadataNotFoundError
  end
end

def http_put(connection, ttl)

def http_put(connection, ttl)
  headers = {
    'User-Agent' => "aws-sdk-ruby3/#{CORE_GEM_VERSION}",
    'x-aws-ec2-metadata-token-ttl-seconds' => ttl.to_s
  }
  request = Net::HTTP::Put.new(METADATA_TOKEN_PATH, headers)
  response = connection.request(request)
  case response.code.to_i
  when 200
    [
      response.body,
      response.header['x-aws-ec2-metadata-token-ttl-seconds'].to_i
    ]
  when 400
    raise TokenRetrievalError
  when 403
    raise RequestForbiddenError
  end
end

def initialize(options = {})

Options Hash: (**options)
  • :backoff (Integer, Proc) -- A backoff used for retryable
  • :http_debug_output (IO) -- An output stream for debugging. Do
  • :http_read_timeout (Integer) -- The number of seconds for
  • :http_open_timeout (Integer) -- The number of seconds to
  • :port (Integer) -- The IMDS endpoint port.
  • :endpoint_mode (String) -- The endpoint mode for
  • :endpoint (String) -- The IMDS
  • :retries (Integer) -- The number of retries for failed
  • :token_ttl (Integer) -- The session token's TTL,

Parameters:
  • options (Hash) --

Other tags:
    See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#instance-metadata-transition-to-version-2 -

Other tags:
    Note: - Customers using containers may need to increase their hop limit
def initialize(options = {})
  @token_ttl = options[:token_ttl] || 21_600
  @retries = options[:retries] || 3
  @backoff = backoff(options[:backoff])
  endpoint_mode = options[:endpoint_mode] || 'IPv4'
  @endpoint = resolve_endpoint(options[:endpoint], endpoint_mode)
  @port = options[:port] || 80
  @http_open_timeout = options[:http_open_timeout] || 1
  @http_read_timeout = options[:http_read_timeout] || 1
  @http_debug_output = options[:http_debug_output]
  @token = nil
  @mutex = Mutex.new
end

def open_connection

def open_connection
  uri = URI.parse(@endpoint)
  http = Net::HTTP.new(uri.hostname || @endpoint, uri.port || @port)
  http.open_timeout = @http_open_timeout
  http.read_timeout = @http_read_timeout
  http.set_debug_output(@http_debug_output) if @http_debug_output
  http.start
  yield(http).tap { http.finish }
end

def resolve_endpoint(endpoint, endpoint_mode)

def resolve_endpoint(endpoint, endpoint_mode)
  return endpoint if endpoint
  case endpoint_mode.downcase
  when 'ipv4' then 'http://169.254.169.254'
  when 'ipv6' then 'http://[fd00:ec2::254]'
  else
    raise ArgumentError,
          ':endpoint_mode is not valid, expected IPv4 or IPv6, '\
          "got: #{endpoint_mode}"
  end
end

def retry_errors(options = {}, &_block)

def retry_errors(options = {}, &_block)
  max_retries = options[:max_retries]
  retries = 0
  begin
    yield
  # These errors should not be retried.
  rescue TokenRetrievalError, MetadataNotFoundError, RequestForbiddenError
    raise
  # StandardError is not ideal but it covers Net::HTTP errors.
  # https://gist.github.com/tenderlove/245188
  rescue StandardError, TokenExpiredError
    raise unless retries < max_retries
    @backoff.call(retries)
    retries += 1
    retry
  end
end