summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/exec.yml2
-rw-r--r--lib/ohai/mixin/oci_metadata.rb69
-rw-r--r--lib/ohai/plugins/cloud.rb22
-rw-r--r--lib/ohai/plugins/oci.rb94
-rw-r--r--spec/unit/mixin/oci_metadata_spec.rb66
-rw-r--r--spec/unit/plugins/cloud_spec.rb38
-rw-r--r--spec/unit/plugins/oci_spec.rb198
7 files changed, 488 insertions, 1 deletions
diff --git a/.github/workflows/exec.yml b/.github/workflows/exec.yml
index 13dca0ca..ba734d5f 100644
--- a/.github/workflows/exec.yml
+++ b/.github/workflows/exec.yml
@@ -6,7 +6,7 @@ name: exec
push:
branches:
- main
- - 1=7-stable
+ - 17-stable
- 16-stable
permissions:
diff --git a/lib/ohai/mixin/oci_metadata.rb b/lib/ohai/mixin/oci_metadata.rb
new file mode 100644
index 00000000..b04243a3
--- /dev/null
+++ b/lib/ohai/mixin/oci_metadata.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+#
+# Author:: Renato Covarrubias (<rnt@rnt.cl>)
+# 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
+ module OCIMetadata
+ OCI_METADATA_ADDR = "169.254.169.254"
+ OCI_METADATA_URL = "/opc/v2"
+ CHASSIS_ASSET_TAG_FILE = "/sys/devices/virtual/dmi/id/chassis_asset_tag"
+
+ # fetch the meta content with a timeout and the required header
+ def http_get(uri)
+ conn = Net::HTTP.start(OCI_METADATA_ADDR)
+ conn.read_timeout = 6
+ conn.get(
+ uri,
+ {
+ "Authorization" => "Bearer Oracle",
+ "User-Agent" => "chef-ohai/#{Ohai::VERSION}",
+ }
+ )
+ end
+
+ # 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 = String(response_body)
+ parser = FFI_Yajl::Parser.new
+ parser.parse(data)
+ rescue FFI_Yajl::ParseError
+ logger.warn("Mixin OciMetadata: Metadata response is NOT valid JSON")
+ nil
+ end
+
+ # Fetch metadata from api
+ def fetch_metadata(metadata = "instance")
+ response = http_get("#{OCI_METADATA_URL}/#{metadata}")
+ return nil unless response.code == "200"
+
+ if response.code == "200"
+ parse_json(response.body)
+ else
+ logger.warn("Mixin OciMetadata: Received response code #{response.code} requesting metadata")
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/lib/ohai/plugins/cloud.rb b/lib/ohai/plugins/cloud.rb
index 8e79a824..503516ab 100644
--- a/lib/ohai/plugins/cloud.rb
+++ b/lib/ohai/plugins/cloud.rb
@@ -28,6 +28,7 @@ Ohai.plugin(:Cloud) do
depends "azure"
depends "digital_ocean"
depends "softlayer"
+ depends "oci"
# Class to help enforce the interface exposed to node[:cloud] (OHAI-542)
#
@@ -336,6 +337,26 @@ Ohai.plugin(:Cloud) do
@cloud_attr_obj.provider = "softlayer"
end
+ # ----------------------------------------
+ # OCI
+ # ----------------------------------------
+
+ # Is current Oracle Cloud Infrastructure?
+ #
+ # === Return
+ # true:: If oci Hash is defined
+ # false:: Otherwise
+ def on_oci?
+ oci != nil
+ end
+
+ # Fill cloud hash with OCI values
+ def oci_values
+ oci["metadata"]["network"]["interface"].each { |vnic| @cloud_attr_obj.add_ipv4_addr(vnic["privateIp"], :private) }
+ @cloud_attr_obj.local_hostname = oci["metadata"]["compute"]["hostname"]
+ @cloud_attr_obj.provider = "oci"
+ end
+
collect_data do
require "ipaddr" unless defined?(IPAddr)
@@ -351,6 +372,7 @@ Ohai.plugin(:Cloud) do
get_digital_ocean_values if on_digital_ocean?
get_softlayer_values if on_softlayer?
get_alibaba_values if on_alibaba?
+ oci_values if on_oci?
cloud @cloud_attr_obj.cloud_mash
end
diff --git a/lib/ohai/plugins/oci.rb b/lib/ohai/plugins/oci.rb
new file mode 100644
index 00000000..04e83ba5
--- /dev/null
+++ b/lib/ohai/plugins/oci.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+#
+# Author:: Renato Covarrubias (<rnt@rnt.cl>)
+# 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.
+#
+
+Ohai.plugin(:Oci) do
+ require_relative "../mixin/oci_metadata"
+ require_relative "../mixin/http_helper"
+
+ include Ohai::Mixin::OCIMetadata
+ include Ohai::Mixin::HttpHelper
+
+ provides "oci"
+
+ collect_data do
+ oci_metadata_from_hints = hint?("oci")
+ if oci_metadata_from_hints
+ logger.trace("Plugin OCI: oci hint is present. Parsing any hint data.")
+ oci Mash.new
+ oci_metadata_from_hints.each { |k, v| oci[k] = v }
+ oci["metadata"] = parse_metadata
+ elsif oci_chassis_asset_tag?
+ logger.trace("Plugin oci: No hints present, but system appears to be on oci.")
+ oci Mash.new
+ oci["metadata"] = parse_metadata
+ else
+ logger.trace("Plugin oci: No hints present and doesn't appear to be on oci.")
+ false
+ end
+ end
+
+ def oci_chassis_asset_tag?
+ has_oci_chassis_asset_tag = false
+ if file_exist?(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE)
+ file_open(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).each do |line|
+ next unless /OracleCloud.com/.match?(line)
+
+ logger.trace("Plugin oci: Found OracleCloud.com chassis_asset_tag used by oci.")
+ has_oci_chassis_asset_tag = true
+ break
+ end
+ end
+ has_oci_chassis_asset_tag
+ end
+
+ def parse_metadata
+ return nil unless can_socket_connect?(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80)
+
+ instance_data = fetch_metadata("instance")
+ return nil if instance_data.nil?
+
+ metadata = Mash.new
+ metadata["compute"] = Mash.new
+
+ instance_data.each do |k, v|
+ metadata["compute"][k] = v
+ end
+
+ vnics_data = fetch_metadata("vnics")
+
+ unless vnics_data.nil?
+ metadata["network"] = Mash.new
+ metadata["network"]["interface"] = []
+ vnics_data.each do |v|
+ metadata["network"]["interface"].append(v)
+ end
+ end
+
+ volume_attachments_data = fetch_metadata("volumeAttachments")
+
+ unless volume_attachments_data.nil?
+ metadata["volumes"] = Mash.new
+ volume_attachments_data.each do |k, v|
+ metadata["volumes"][k] = v
+ end
+ end
+
+ metadata
+ end
+end
diff --git a/spec/unit/mixin/oci_metadata_spec.rb b/spec/unit/mixin/oci_metadata_spec.rb
new file mode 100644
index 00000000..3135b028
--- /dev/null
+++ b/spec/unit/mixin/oci_metadata_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+#
+# Author:: Renato Covarrubias <rnt@rnt.cl>
+# 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 "ohai/mixin/oci_metadata"
+
+describe Ohai::Mixin::OCIMetadata do
+ let(:mixin) do
+ mixin = Object.new.extend(Ohai::Mixin::OCIMetadata)
+ mixin
+ end
+
+ before do
+ logger = instance_double("Mixlib::Log::Child", trace: nil, debug: nil, warn: nil)
+ allow(mixin).to receive(:logger).and_return(logger)
+ end
+
+ describe "#http_get" do
+ it "gets the passed URI" do
+ http_mock = double("http")
+ allow(http_mock).to receive(:read_timeout=)
+ allow(Net::HTTP).to receive(:start).with(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR).and_return(http_mock)
+
+ expect(http_mock).to receive(:get).with(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR,
+ { "Authorization" => "Bearer Oracle",
+ "User-Agent" => "chef-ohai/#{Ohai::VERSION}" })
+ mixin.http_get(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR)
+ end
+ end
+
+ describe "#fetch_metadata" do
+ it "returns an empty hash given a non-200 response" do
+ http_mock = double("http", { code: "404" })
+ allow(mixin).to receive(:http_get).and_return(http_mock)
+
+ expect(mixin.logger).not_to receive(:warn)
+ vals = mixin.fetch_metadata
+ expect(vals).to eq(nil)
+ end
+
+ it "returns a populated hash given valid JSON response" do
+ http_mock = double("http", { code: "200", body: '{ "foo": "bar"}' })
+ allow(mixin).to receive(:http_get).and_return(http_mock)
+
+ expect(mixin.logger).not_to receive(:warn)
+ vals = mixin.fetch_metadata
+ expect(vals).to eq({ "foo" => "bar" })
+ end
+ end
+end
diff --git a/spec/unit/plugins/cloud_spec.rb b/spec/unit/plugins/cloud_spec.rb
index 95d210e0..82dee74f 100644
--- a/spec/unit/plugins/cloud_spec.rb
+++ b/spec/unit/plugins/cloud_spec.rb
@@ -87,6 +87,7 @@ describe Ohai::System, "plugin cloud" do
@plugin[:gce] = nil
@plugin[:digital_ocean] = nil
@plugin[:softlayer] = nil
+ @plugin[:oci] = nil
@plugin.run
expect(@plugin[:cloud]).to be_nil
end
@@ -511,4 +512,41 @@ describe Ohai::System, "plugin cloud" do
end
end
+ describe "with OCI mash" do
+ before do
+ @plugin[:oci] = Mash.new
+ @plugin[:oci][:metadata] = {
+ "compute" => {
+ "hostname" => "my-hostname",
+ },
+ "network" => {
+ "interface" => [
+ { "vnicId" => "ocid1.vnic.oc1.phx.exampleuniqueID", "privateIp" => "10.0.3.6", "vlanTag" => 11,
+ "macAddr" => "00:00:00:00:00:01", "virtualRouterIp" => "10.0.3.1", "subnetCidrBlock" => "10.0.3.0/24",
+ "nicIndex" => 0 },
+ ],
+ },
+ }
+ end
+
+ it "doesn't populates cloud vm_name" do
+ @plugin.run
+ expect(@plugin[:cloud][:vm_name]).not_to eq("testtest")
+ end
+
+ it "populates cloud local_hostname" do
+ @plugin.run
+ expect(@plugin[:cloud][:local_hostname]).to eq("my-hostname")
+ end
+
+ it "populates cloud private ip" do
+ @plugin.run
+ expect(@plugin[:cloud][:local_ipv4]).to eq(@plugin[:oci][:metadata][:network][:interface][0]["privateIp"])
+ end
+
+ it "populates cloud provider" do
+ @plugin.run
+ expect(@plugin[:cloud][:provider]).to eq("oci")
+ end
+ end
end
diff --git a/spec/unit/plugins/oci_spec.rb b/spec/unit/plugins/oci_spec.rb
new file mode 100644
index 00000000..53737383
--- /dev/null
+++ b/spec/unit/plugins/oci_spec.rb
@@ -0,0 +1,198 @@
+# frozen_string_literal: true
+
+#
+# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
+# 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 "spec_helper"
+begin
+ require "win32/registry" unless defined?(Win32::Registry)
+rescue LoadError => e
+ puts "Skipping missing rake dep: #{e}"
+end
+
+describe Ohai::System, "plugin oci" do
+ let(:plugin) { get_plugin("oci") }
+ let(:hint) do
+ {
+ "local_hostname" => "test-vm",
+ "provider" => "oci",
+ }
+ end
+
+ let(:response_data) do
+ {
+ "compute" => {
+ "availabilityDomain" => "EMIr:PHX-AD-1",
+ "faultDomain" => "FAULT-DOMAIN-3",
+ "compartmentId" => "ocid1.tenancy.oc1..exampleuniqueID",
+ "displayName" => "my-example-instance",
+ "hostname" => "my-hostname",
+ "id" => "ocid1.instance.oc1.phx.exampleuniqueID",
+ "image" => "ocid1.image.oc1.phx.exampleuniqueID",
+ "metadata" => {
+ "ssh_authorized_keys" => "example-ssh-key",
+ },
+ "region" => "phx",
+ "canonicalRegionName" => "us-phoenix-1",
+ "ociAdName" => "phx-ad-1",
+ "regionInfo" => {
+ "realmKey" => "oc1",
+ "realmDomainComponent" => "oraclecloud.com",
+ "regionKey" => "PHX",
+ "regionIdentifier" => "us-phoenix-1",
+ },
+ "shape" => "VM.Standard.E3.Flex",
+ "state" => "Running",
+ "timeCreated" => 1_600_381_928_581,
+ "agentConfig" => {
+ "monitoringDisabled" => false,
+ "managementDisabled" => false,
+ "allPluginsDisabled" => false,
+ "pluginsConfig" => [
+ { "name" => "OS Management Service Agent", "desiredState" => "ENABLED" },
+ { "name" => "Custom Logs Monitoring", "desiredState" => "ENABLED" },
+ { "name" => "Compute Instance Run Command", "desiredState" => "ENABLED" },
+ { "name" => "Compute Instance Monitoring", "desiredState" => "ENABLED" },
+ ],
+ },
+ "freeformTags" => {
+ "Department" => "Finance",
+ },
+ "definedTags" => {
+ "Operations" => {
+ "CostCenter" => "42",
+ },
+ },
+ },
+ "network" => {
+ "interface" => [
+ { "vnicId" => "ocid1.vnic.oc1.phx.exampleuniqueID", "privateIp" => "10.0.3.6", "vlanTag" => 11,
+ "macAddr" => "00:00:00:00:00:01", "virtualRouterIp" => "10.0.3.1", "subnetCidrBlock" => "10.0.3.0/24",
+ "nicIndex" => 0 },
+ { "vnicId" => "ocid1.vnic.oc1.phx.exampleuniqueID", "privateIp" => "10.0.4.3", "vlanTag" => 12,
+ "macAddr" => "00:00:00:00:00:02", "virtualRouterIp" => "10.0.4.1", "subnetCidrBlock" => "10.0.4.0/24",
+ "nicIndex" => 0 },
+ ],
+ },
+ }
+ end
+
+ before do
+ # skips all the metadata logic unless we want to test it
+ allow(plugin).to receive(:can_socket_connect?)
+ .with(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80)
+ .and_return(false)
+ end
+
+ shared_examples_for "!oci" do
+ it "does not set the oci attribute" do
+ plugin.run
+ expect(plugin[:oci]).to be_nil
+ end
+ end
+
+ shared_examples_for "oci" do
+ it "sets the oci attribute" do
+ plugin.run
+ expect(plugin[:oci]).to be_truthy
+ expect(plugin[:oci]).to have_key(:metadata)
+ end
+ end
+
+ describe "with oci hint file" do
+ before do
+ allow(plugin).to receive(:hint?).with("oci").and_return(hint)
+ end
+
+ it "sets the oci cloud attributes" do
+ plugin.run
+ expect(plugin[:oci]["provider"]).to eq("oci")
+ expect(plugin[:oci]["local_hostname"]).to eq("test-vm")
+ end
+ end
+
+ describe "without oci hint file not in OCI" do
+ before do
+ allow(plugin).to receive(:hint?).with("oci").and_return(false)
+ allow(plugin).to receive(:file_exist?).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(true)
+ @double_file = double(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE)
+ allow(@double_file).to receive(:each)
+ .and_yield("")
+ allow(plugin).to receive(:file_open).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(@double_file)
+ end
+
+ it_behaves_like "!oci"
+ end
+
+ describe "without oci hint file in OCI" do
+ before do
+ allow(plugin).to receive(:hint?).with("oci").and_return(false)
+ allow(plugin).to receive(:file_exist?).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(true)
+ @double_file = double(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE)
+ allow(@double_file).to receive(:each)
+ .and_yield("OracleCloud.com")
+ allow(plugin).to receive(:file_open).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(@double_file)
+ end
+
+ it_behaves_like "oci"
+ end
+
+ describe "with non-responsive metadata endpoint" do
+ before do
+ allow(plugin).to receive(:hint?).with("oci").and_return({})
+ end
+
+ it "does not return metadata information" do
+ allow(plugin).to receive(:can_socket_connect?)
+ .with(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80)
+ .and_return(true)
+ allow(plugin).to receive(:parse_metadata).and_return(nil)
+
+ plugin.run
+ expect(plugin[:oci]).to have_key(:metadata)
+ expect(plugin[:oci][:metadata]).to be_nil
+ end
+ end
+
+ describe "with responsive metadata endpoint" do
+ before do
+ allow(plugin).to receive(:hint?).with("oci").and_return({})
+ allow(plugin).to receive(:can_socket_connect?)
+ .with(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80)
+ .and_return(true)
+ allow(plugin).to receive(:parse_metadata).and_return(response_data)
+ plugin.run
+ end
+
+ it "returns metadata compute information" do
+ expect(plugin[:oci][:metadata][:compute][:availabilityDomain]).to eq("EMIr:PHX-AD-1")
+ expect(plugin[:oci][:metadata][:compute][:compartmentId]).to eq("ocid1.tenancy.oc1..exampleuniqueID")
+ expect(plugin[:oci][:metadata][:compute][:faultDomain]).to eq("FAULT-DOMAIN-3")
+ expect(plugin[:oci][:metadata][:compute][:hostname]).to eq("my-hostname")
+ expect(plugin[:oci][:metadata][:compute][:image]).to eq("ocid1.image.oc1.phx.exampleuniqueID")
+ expect(plugin[:oci][:metadata][:compute][:region]).to eq("phx")
+ expect(plugin[:oci][:metadata][:compute][:shape]).to eq("VM.Standard.E3.Flex")
+ expect(plugin[:oci][:metadata][:compute][:state]).to eq("Running")
+ end
+
+ it "returns metadata network information" do
+ expect(plugin[:oci][:metadata][:network][:interface][0][:macAddr]).to eq("00:00:00:00:00:01")
+ expect(plugin[:oci][:metadata][:network][:interface][0][:privateIp]).to eq("10.0.3.6")
+ end
+ end
+end