module Ohai::Mixin::Ec2Metadata
This code parses the EC2 Instance Metadata API to provide details of the running instance.
Earlier version of this code assumed a specific version of the metadata API was available. Unfortunately the API versions supported by a particular instance are determined at instance launch and are not extended over the life of the instance. As such the earlier code would fail depending on the age of the instance.
The updated code probes the instance metadata endpoint for available versions, determines the most advanced version known to work and executes the metadata retrieval using that version.
If no compatible version is found, an empty hash is returned.
Constants
- EC2_ARRAY_DIR
- EC2_ARRAY_VALUES
- EC2_JSON_DIR
- EC2_METADATA_ADDR
- EC2_SUPPORTED_VERSIONS
Public Instance Methods
best_api_version()
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 77 def best_api_version response = http_client.get("/") unless response.code == '200' raise "Unable to determine EC2 metadata version (returned #{response.code} response)" end # Note: Sorting the list of versions may have unintended consequences in # non-EC2 environments. It appears to be safe in EC2 as of 2013-04-12. versions = response.body.split("\n") versions = response.body.split("\n").sort until (versions.empty? || EC2_SUPPORTED_VERSIONS.include?(versions.last)) do pv = versions.pop Ohai::Log.debug("EC2 shows unsupported metadata version: #{pv}") unless pv == 'latest' end Ohai::Log.debug("EC2 metadata version: #{versions.last}") if versions.empty? raise "Unable to determine EC2 metadata version (no supported entries found)" end versions.last end
can_metadata_connect?(addr, port, timeout=2)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 51 def can_metadata_connect?(addr, port, timeout=2) t = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0) saddr = Socket.pack_sockaddr_in(port, addr) connected = false begin t.connect_nonblock(saddr) rescue Errno::EINPROGRESS r,w,e = IO::select(nil,[t],nil,timeout) if !w.nil? connected = true else begin t.connect_nonblock(saddr) rescue Errno::EISCONN t.close connected = true rescue SystemCallError end end rescue SystemCallError end Ohai::Log.debug("can_metadata_connect? == #{connected}") connected end
fetch_dir_metadata(id, api_version)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 155 def fetch_dir_metadata(id, api_version) metadata = Hash.new retrieved_metadata = metadata_get(id, api_version) if retrieved_metadata retrieved_metadata.split("\n").each do |o| key = expand_path(o) if key[-1..-1] != '/' retr_meta = metadata_get("#{id}#{key}", api_version) metadata[metadata_key(key)] = retr_meta ? retr_meta : '' elsif not key.eql?('/') metadata[key[0..-2]] = fetch_dir_metadata("#{id}#{key}", api_version) end end metadata end end
fetch_json_dir_metadata(id, api_version)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 172 def fetch_json_dir_metadata(id, api_version) metadata = Hash.new retrieved_metadata = metadata_get(id, api_version) if retrieved_metadata retrieved_metadata.split("\n").each do |o| key = expand_path(o) if key[-1..-1] != '/' retr_meta = metadata_get("#{id}#{key}", api_version) data = retr_meta ? retr_meta : '' json = StringIO.new(data) parser = FFI_Yajl::Parser.new metadata[metadata_key(key)] = parser.parse(json) elsif not key.eql?('/') metadata[key[0..-2]] = fetch_json_dir_metadata("#{id}#{key}", api_version) end end metadata end end
fetch_metadata(id='', api_version=nil)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 122 def fetch_metadata(id='', api_version=nil) api_version ||= best_api_version return Hash.new if api_version.nil? metadata = Hash.new retrieved_metadata = metadata_get(id, api_version) if retrieved_metadata retrieved_metadata.split("\n").each do |o| key = expand_path("#{id}#{o}") if key[-1..-1] != '/' metadata[metadata_key(key)] = if EC2_ARRAY_VALUES.include? key retr_meta = metadata_get(key, api_version) retr_meta ? retr_meta.split("\n") : retr_meta else metadata_get(key, api_version) end elsif not key.eql?(id) and not key.eql?('/') name = key[0..-2] sym = metadata_key(name) if EC2_ARRAY_DIR.include?(name) metadata[sym] = fetch_dir_metadata(key, api_version) elsif EC2_JSON_DIR.include?(name) metadata[sym] = fetch_json_dir_metadata(key, api_version) else fetch_metadata(key, api_version).each{|k,v| metadata[k] = v} end end end metadata end end
fetch_userdata()
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 192 def fetch_userdata() api_version = best_api_version return nil if api_version.nil? response = http_client.get("/#{api_version}/user-data/") response.code == "200" ? response.body : nil end
http_client()
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 97 def http_client Net::HTTP.start(EC2_METADATA_ADDR).tap {|h| h.read_timeout = 600} end
metadata_get(id, api_version)
click to toggle source
Get metadata for a given path and API version
@details
Typically, a 200 response is expected for valid metadata. On certain instance types, traversing the provided metadata path produces a 404 for some unknown reason. In that event, return `nil` and continue the run instead of failing it.
# File lib/ohai/mixin/ec2_metadata.rb, line 108 def metadata_get(id, api_version) path = "/#{api_version}/meta-data/#{id}" response = http_client.get(path) case response.code when '200' response.body when '404' Ohai::Log.debug("Encountered 404 response retreiving EC2 metadata path: #{path} ; continuing.") nil else raise "Encountered error retrieving EC2 metadata (#{path} returned #{response.code} response)" end end
Private Instance Methods
expand_path(file_name)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 201 def expand_path(file_name) path = file_name.gsub(/\=.*$/, '/') # ignore "./" and "../" path.gsub(%r{/\.\.?(?:/|$)}, '/'). sub(%r{^\.\.?(?:/|$)}, ''). sub(%r{^$}, '/') end
metadata_key(key)
click to toggle source
# File lib/ohai/mixin/ec2_metadata.rb, line 209 def metadata_key(key) key.gsub(/\-|\//, '_') end