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)
-
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)
-
: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