diff options
author | Tim Smith <tsmith@chef.io> | 2021-02-10 18:04:03 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-10 18:04:03 -0800 |
commit | 2e48aed6265f1830bcfdd640d8cfe2373fc99c58 (patch) | |
tree | d98b67eaf3571ef24bbf0e2d2cbcdbbd04aa90ea | |
parent | ba09f9b476b37c9c07e8d572c8fdba73450fbd06 (diff) | |
parent | 15b1c965cec56b69a0179f927230f329a3065b78 (diff) | |
download | ohai-2e48aed6265f1830bcfdd640d8cfe2373fc99c58.tar.gz |
Merge pull request #1620 from chef/alibaba
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | lib/ohai/mixin/alibaba_metadata.rb | 86 | ||||
-rw-r--r-- | lib/ohai/plugins/alibaba.rb | 72 | ||||
-rw-r--r-- | lib/ohai/plugins/cloud.rb | 19 | ||||
-rw-r--r-- | spec/unit/plugins/alibaba_spec.rb | 89 | ||||
-rw-r--r-- | spec/unit/plugins/cloud_spec.rb | 31 |
5 files changed, 296 insertions, 1 deletions
diff --git a/lib/ohai/mixin/alibaba_metadata.rb b/lib/ohai/mixin/alibaba_metadata.rb new file mode 100644 index 00000000..983dfd91 --- /dev/null +++ b/lib/ohai/mixin/alibaba_metadata.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "net/http" unless defined?(Net::HTTP) + +module Ohai + module Mixin + # + # This code parses the Alibaba Instance Metadata API to provide details + # of the running instance. + # + # Note: As of 2021-02-07 there is only one API release so we're not implementing + # logic like the ec2 or azure mixins where we have to find the latest supported + # release + module AlibabaMetadata + + ALI_METADATA_ADDR ||= "100.100.100.200" + + def http_get(uri) + conn = Net::HTTP.start(ALI_METADATA_ADDR) + conn.read_timeout = 6 + conn.keep_alive_timeout = 6 + conn.get("/2016-01-01/#{uri}", { "User-Agent" => "chef-ohai/#{Ohai::VERSION}" }) + end + + def fetch_metadata(id = "") + response = http_get(id) + return nil unless response.code == "200" + + if json?(response.body) + data = String(response.body) + parser = FFI_Yajl::Parser.new + parser.parse(data) + elsif response.body.include?("\n") + temp = {} + response.body.split("\n").each do |sub_attr| + temp[sanitize_key(sub_attr)] = fetch_metadata("#{id}/#{sub_attr}") + end + temp + else + response.body + end + end + + # @param [String] data that might be JSON + # + # @return [Boolean] is the data JSON or not? + def json?(data) + data = String(data) + parser = FFI_Yajl::Parser.new + begin + parser.parse(data) + true + rescue FFI_Yajl::ParseError + false + end + end + + # @param data [String] + # + # @return [Boolean] is there a trailing /? + def has_trailing_slash?(data) + !!( data =~ %r{/$} ) + end + + def sanitize_key(key) + key.gsub(%r{\-|/}, "_") + end + end + end +end diff --git a/lib/ohai/plugins/alibaba.rb b/lib/ohai/plugins/alibaba.rb new file mode 100644 index 00000000..ac3c8088 --- /dev/null +++ b/lib/ohai/plugins/alibaba.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# How we detect Alibaba from easiest to hardest & least reliable +# 1. Ohai alibaba hint exists. This always works +# 2. DMI sys_vendor data mentions alibaba. + +Ohai.plugin(:Alibaba) do + require_relative "../mixin/alibaba_metadata" + require_relative "../mixin/http_helper" + + include Ohai::Mixin::AlibabaMetadata + include Ohai::Mixin::HttpHelper + + provides "alibaba" + + # look for alibaba string in dmi sys_vendor data within the sys tree. + # this works even if the system lacks dmidecode use by the Dmi plugin + # @return [Boolean] do we have Alibaba DMI data? + def has_ali_dmi? + if /Alibaba/.match?(file_val_if_exists("/sys/class/dmi/id/sys_vendor")) + logger.trace("Plugin Alibaba: has_ali_dmi? == true") + true + else + logger.trace("Plugin Alibaba: has_ali_dmi? == false") + false + end + end + + # return the contents of a file if the file exists + # @param path[String] abs path to the file + # @return [String] contents of the file if it exists + def file_val_if_exists(path) + if file_exist?(path) + file_read(path) + end + end + + # a single check that combines all the various detection methods for Alibaba + # @return [Boolean] Does the system appear to be on Alibaba + def looks_like_alibaba? + return true if hint?("alibaba") || has_ali_dmi? + end + + collect_data do + if looks_like_alibaba? + logger.trace("Plugin Alibaba: looks_like_alibaba? == true") + alibaba Mash.new + fetch_metadata.each do |k, v| + alibaba[k] = v + end + else + logger.trace("Plugin Alibaba: looks_like_alibaba? == false") + false + end + end +end diff --git a/lib/ohai/plugins/cloud.rb b/lib/ohai/plugins/cloud.rb index 14e1e454..be7a8405 100644 --- a/lib/ohai/plugins/cloud.rb +++ b/lib/ohai/plugins/cloud.rb @@ -18,6 +18,7 @@ Ohai.plugin(:Cloud) do provides "cloud" + depends "alibaba" depends "ec2" depends "gce" depends "rackspace" @@ -118,7 +119,22 @@ Ohai.plugin(:Cloud) do end end - #--------------------------------------- + #-------------------------------------- + # Alibaba Cloud + #-------------------------------------- + + def on_alibaba? + alibaba != nil + end + + def get_alibaba_values + @cloud_attr_obj.add_ipv4_addr(alibaba["meta_data"]["eipv4"], :public) + @cloud_attr_obj.add_ipv4_addr(alibaba["meta_data"]["private_ipv4"], :private) + @cloud_attr_obj.local_hostname = alibaba["meta_data"]["hostname"] + @cloud_attr_obj.provider = "alibaba" + end + + #-------------------------------------- # Google Compute Engine (gce) #-------------------------------------- @@ -334,6 +350,7 @@ Ohai.plugin(:Cloud) do get_azure_values if on_azure? get_digital_ocean_values if on_digital_ocean? get_softlayer_values if on_softlayer? + get_alibaba_values if on_alibaba? cloud @cloud_attr_obj.cloud_mash end diff --git a/spec/unit/plugins/alibaba_spec.rb b/spec/unit/plugins/alibaba_spec.rb new file mode 100644 index 00000000..c77e9899 --- /dev/null +++ b/spec/unit/plugins/alibaba_spec.rb @@ -0,0 +1,89 @@ +# +# Author:: Ranjib Dey (dey.ranjib@gmail.com) +# Author:: Tim Smith (tsmith@chef.io) +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDIT"Net::HTTP Response"NS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" +require "open-uri" + +describe Ohai::System, "plugin alibaba" do + let(:plugin) { get_plugin("alibaba") } + + before do + allow(plugin).to receive(:hint?).with("alibaba").and_return(false) + allow(plugin).to receive(:file_exist?).with("/sys/class/dmi/id/sys_vendor").and_return(false) + end + + shared_examples_for "!alibaba" do + it "does NOT attempt to fetch the alibaba metadata" do + expect(plugin).not_to receive(:http_get) + plugin.run + end + + it "does NOT set alibaba attributes" do + expect(plugin[:alibaba]).to be_nil + plugin.run + end + end + + shared_examples_for "alibaba" do + before do + @http_get = double("Net::HTTP client") + allow(plugin).to receive(:http_get).with("").and_return(double("Net::HTTP Response", body: "meta-data\n", code: "200")) + allow(plugin).to receive(:http_get).with("/meta-data").and_return(double("Net::HTTP Response", body: "hostname\n", code: "200")) + allow(plugin).to receive(:http_get).with("/meta-data/hostname").and_return(double("Net::HTTP Response", body: "foo", code: "200")) + allow(IO).to receive(:select).and_return([[], [1], []]) + t = double("connection") + allow(t).to receive(:connect_nonblock).and_raise(Errno::EINPROGRESS) + allow(Socket).to receive(:new).and_return(t) + allow(Socket).to receive(:pack_sockaddr_in).and_return(nil) + end + + it "recursively fetches and properly parses json metadata" do + plugin.run + + expect(plugin[:alibaba]).not_to be_nil + expect(plugin[:alibaba]["meta_data"]).to eq("hostname" => "foo") + end + + end + + describe "with hint file and with metadata connection" do + before do + allow(plugin).to receive(:hint?).with("alibaba").and_return({}) + end + + it_behaves_like "alibaba" + end + + describe "with alibaba dmi sys_vendor data" do + before do + allow(plugin).to receive(:file_exist?).with("/sys/class/dmi/id/sys_vendor").and_return(true) + allow(plugin).to receive(:file_read).with("/sys/class/dmi/id/sys_vendor").and_return("Alibaba Cloud\n") + end + + it_behaves_like "alibaba" + end + + describe "without hint file and non-alibaba dmi sys_vendor data" do + before do + allow(plugin).to receive(:file_exist?).with("/sys/class/dmi/id/sys_vendor").and_return(true) + allow(plugin).to receive(:file_read).with("/sys/class/dmi/id/sys_vendor").and_return("TimCloud\n") + end + + it_behaves_like "!alibaba" + end +end diff --git a/spec/unit/plugins/cloud_spec.rb b/spec/unit/plugins/cloud_spec.rb index f71a7dd3..95d210e0 100644 --- a/spec/unit/plugins/cloud_spec.rb +++ b/spec/unit/plugins/cloud_spec.rb @@ -78,6 +78,7 @@ describe Ohai::System, "plugin cloud" do describe "with no cloud mashes" do it "doesn't populate the cloud data" do + @plugin[:alibaba] = nil @plugin[:ec2] = nil @plugin[:rackspace] = nil @plugin[:eucalyptus] = nil @@ -91,6 +92,36 @@ describe Ohai::System, "plugin cloud" do end end + describe "with Alibaba mash" do + before do + @plugin[:alibaba] = Mash.new + @plugin[:alibaba]["meta_data"] = Mash.new + end + + it "populates hostname" do + @plugin[:alibaba]["meta_data"]["hostname"] = "foo" + @plugin.run + expect(@plugin[:cloud][:local_hostname]).to eq("foo") + end + + it "populates cloud public ip" do + @plugin[:alibaba]["meta_data"]["eipv4"] = "174.129.150.8" + @plugin.run + expect(@plugin[:cloud][:public_ipv4_addrs][0]).to eq("174.129.150.8") + end + + it "populates cloud private ip" do + @plugin[:alibaba]["meta_data"]["private_ipv4"] = "10.252.42.149" + @plugin.run + expect(@plugin[:cloud][:local_ipv4_addrs][0]).to eq("10.252.42.149") + end + + it "populates cloud provider" do + @plugin.run + expect(@plugin[:cloud][:provider]).to eq("alibaba") + end + end + describe "with EC2 mash" do before do @plugin[:ec2] = Mash.new |