diff options
author | Tim Smith <tsmith84@gmail.com> | 2020-01-29 16:54:50 -0800 |
---|---|---|
committer | Tim Smith <tsmith84@gmail.com> | 2020-03-02 12:42:53 -0800 |
commit | 808ce32e95c9e4799d1dabbbdb155d6484e31b28 (patch) | |
tree | e9a644287351830741d091fa46224c913a90c801 /lib/ohai/mixin | |
parent | c5bfea7486beb649091d9248310252ae2c0ab416 (diff) | |
download | ohai-808ce32e95c9e4799d1dabbbdb155d6484e31b28.tar.gz |
Support fetching the latest version of Azure metadatanew_azure_versions
This is inspired by the way we do this in EC2, but it's different in
many ways since Azure presents the latest version in a different way.
Signed-off-by: Tim Smith <tsmith@chef.io>
Diffstat (limited to 'lib/ohai/mixin')
-rw-r--r-- | lib/ohai/mixin/azure_metadata.rb | 87 |
1 files changed, 72 insertions, 15 deletions
diff --git a/lib/ohai/mixin/azure_metadata.rb b/lib/ohai/mixin/azure_metadata.rb index 5e924b4b..903c3381 100644 --- a/lib/ohai/mixin/azure_metadata.rb +++ b/lib/ohai/mixin/azure_metadata.rb @@ -1,6 +1,6 @@ # # Author:: Tim Smith (<tsmith@chef.io>) -# Copyright:: Copyright 2017 Chef Software, Inc. +# Copyright:: 2017-2020 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,30 +19,87 @@ require "net/http" unless defined?(Net::HTTP) module Ohai module Mixin + # + # This code parses the Azure Instance Metadata API to provide details + # of the running instance. + # + # The 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. + # module AzureMetadata AZURE_METADATA_ADDR ||= "169.254.169.254".freeze - AZURE_METADATA_URL ||= "/metadata/instance?api-version=2017-08-01".freeze - # fetch the meta content with a timeout and the required header + # it's important that the newer versions are at the end of this array so we can skip sorting it + AZURE_SUPPORTED_VERSIONS ||= %w{ 2017-04-02 2017-08-01 2017-12-01 2018-02-01 2018-04-02 + 2018-10-01 2019-02-01 2019-03-11 2019-04-30 2019-06-01 + 2019-06-04 2019-08-01 2019-08-15 2019-11-01 }.freeze + + def best_api_version + @api_version ||= begin + logger.trace("Mixin AzureMetadata: Fetching http://#{AZURE_METADATA_ADDR}/metadata/instance to determine the latest supported metadata release") + response = http_get("/metadata/instance") + if response.code == "404" + logger.trace("Mixin AzureMetadata: Received HTTP 404 from metadata server while determining API version, assuming #{AZURE_SUPPORTED_VERSIONS[-1]}") + return AZURE_SUPPORTED_VERSIONS.last + elsif response.code != "400" # 400 is actually what we want + raise "Mixin AzureMetadata: Unable to determine Azure metadata version (returned #{response.code} response)" + end + + # azure returns a list of the 3 latest versions it supports + versions = parse_json(response.body)["newest-versions"] + versions.sort! + + until versions.empty? || AZURE_SUPPORTED_VERSIONS.include?(versions.last) + pv = versions.pop + logger.trace("Mixin AzureMetadata: Azure metadata version #{pv} is not present in the versions provided by the Azure Instance Metadata service") + end + + if versions.empty? + logger.debug "Mixin AzureMetadata: The short list of supported versions provided by Azure Instance Metadata service doesn't match any known versions to Ohai. Using the latest supported release known to Ohai instead: #{AZURE_SUPPORTED_VERSIONS.last}" + return AZURE_SUPPORTED_VERSIONS.last + end + + logger.trace("Mixin AzureMetadata: Latest supported Azure metadata version: #{versions.last}") + versions.last + end + end + + # fetch the meta content with a timeout and the required header and a read timeout of 6s + # + # @param [String] the relative uri to fetch from the Azure Metadata Service URL + # + # @return [Net::HTTP] def http_get(uri) + full_uri = URI.join("http://#{AZURE_METADATA_ADDR}", uri) conn = Net::HTTP.start(AZURE_METADATA_ADDR) conn.read_timeout = 6 - conn.get(uri, { "Metadata" => "true" }) + conn.get(full_uri, { "Metadata" => "true" }) end - def fetch_metadata - logger.trace("Mixin AzureMetadata: Fetching metadata from host #{AZURE_METADATA_ADDR} at #{AZURE_METADATA_URL}") - response = http_get(AZURE_METADATA_URL) + # parse JSON data from a String to a Hash + # + # @param [String] response_body json as string to parse + # + # @return [Hash] + def parse_json(response_body) + data = StringIO.new(response_body) + parser = FFI_Yajl::Parser.new + parser.parse(data) + rescue FFI_Yajl::ParseError + logger.warn("Mixin AzureMetadata: Metadata response is NOT valid JSON") + nil + end + + def fetch_metadata(api_version = nil) + metadata_url = "/metadata/instance?api-version=#{best_api_version}" + logger.trace("Mixin AzureMetadata: Fetching metadata from host #{AZURE_METADATA_ADDR} at #{metadata_url}") + response = http_get(metadata_url) if response.code == "200" - begin - data = StringIO.new(response.body) - parser = FFI_Yajl::Parser.new - parser.parse(data) - rescue FFI_Yajl::ParseError - logger.warn("Mixin AzureMetadata: Metadata response is NOT valid JSON") - nil - end + parse_json(response.body) else logger.warn("Mixin AzureMetadata: Received response code #{response.code} requesting metadata") nil |