lib/ohai/system.rb in ohai-18.0.26 vs lib/ohai/system.rb in ohai-18.1.0

- old
+ new

@@ -1,258 +1,258 @@ -# frozen_string_literal: true -# -# Author:: Adam Jacob (<adam@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_relative "version" -require_relative "loader" -require_relative "log" -require_relative "mash" -require_relative "runner" -require_relative "dsl" -require_relative "mixin/shell_out" -require_relative "mixin/os" -require_relative "mixin/string" -require_relative "mixin/constant_helper" -require_relative "provides_map" -require_relative "hints" -require_relative "config" -require_relative "train_transport" -require "ffi_yajl" unless defined?(FFI_Yajl) -require "cgi" unless defined?(CGI) - -module Ohai - # The class used by Ohai::Application and Chef to actually collect data - class System - include Ohai::Mixin::ConstantHelper - - attr_accessor :data - attr_reader :config - attr_reader :provides_map - attr_reader :logger - attr_writer :transport_connection - - # the cli flag is used to determine if we're being constructed by - # something like chef-client (which doesn't set this flag) and - # which sets up its own loggers, or if we're coming from Ohai::Application - # and therefore need to configure Ohai's own logger. - def initialize(config = {}) - @cli = config[:invoked_from_cli] - @plugin_path = "" - @config = config - @failed_plugins = [] - @logger = config[:logger] || Ohai::Log.with_child - @logger.metadata = { system: "ohai", version: Ohai::VERSION } - reset_system - end - - # clears the current collected data, clears the provides map for plugins, - # refreshes hints, and reconfigures ohai. In short this gets Ohai into a first run state - # - # @return [void] - def reset_system - @data = Mash.new - @provides_map = ProvidesMap.new - - configure_ohai - configure_logging if @cli - - @loader = Ohai::Loader.new(self) - - Ohai::Hints.refresh_hints - - # Remove the previously defined plugins - recursive_remove_constants(Ohai::NamedPlugin) - end - - def runner - @runner ||= - Ohai::Runner.new(self, true).tap do |runner| - runner.transport_connection = transport_connection unless transport_connection.nil? - end - end - - def [](key) - @data[key] - end - - # Resets the system and loads then runs the plugins. This is the primary method called - # to run the system. - # - # @param [Array<String>] attribute_filter the attributes to run. All will be run if not specified - # - # @return [void] - def all_plugins(attribute_filter = nil) - # Reset the system when all_plugins is called since this function - # can be run multiple times in order to pick up any changes in the - # config or plugins with Chef. - reset_system - - load_plugins - run_plugins(true, attribute_filter) - end - - # load all plugins by calling Ohai::Loader.load_all - # - # @see Ohai::Loader.load_all - def load_plugins - @loader.load_all - end - - # get backend parameters for target mode - # - # @return [Train::Transport] - def transport_connection - @transport_connection ||= Ohai::TrainTransport.new(logger).build_transport&.connection - end - - # run all plugins or those that match the attribute filter is provided - # - # @param safe [Boolean] - # @param [Array<String>] attribute_filter the attributes to run. All will be run if not specified - # - # @return [Mash] - def run_plugins(safe = false, attribute_filter = nil) - begin - @provides_map.all_plugins(attribute_filter).each do |plugin| - runner.run_plugin(plugin) - end - transport_connection.close unless transport_connection.nil? - rescue Ohai::Exceptions::AttributeNotFound, Ohai::Exceptions::DependencyCycle => e - logger.error("Encountered error while running plugins: #{e.inspect}") - raise - end - critical_failed = Ohai::Config.ohai[:critical_plugins] & runner.failed_plugins - unless critical_failed.empty? - msg = "The following Ohai plugins marked as critical failed: #{critical_failed}" - if @cli - logger.error(msg) - exit(true) - else - raise Ohai::Exceptions::CriticalPluginFailure, "#{msg}. Failing Chef run." - end - end - - # Freeze all strings. - freeze_strings! - end - - # @param [String] plugin_path - # - # @return [void] - # - def run_additional_plugins(plugin_path) - @loader.load_additional(plugin_path).each do |plugin| - logger.trace "Running plugin #{plugin}" - runner.run_plugin(plugin) - end - - freeze_strings! - end - - # - # Serialize this object as a hash - # - def to_json - FFI_Yajl::Encoder.new.encode(@data) - end - - # - # Pretty Print this object as JSON - # - def json_pretty_print(item = nil) - FFI_Yajl::Encoder.new(pretty: true, validate_utf8: false).encode(item || @data) - end - - def attributes_print(a) - data = @data - a.split("/").each do |part| - data = data[part] - end - raise ArgumentError, "I cannot find an attribute named #{a}!" if data.nil? - - case data - when Hash, Mash, Array, Integer - json_pretty_print(data) - when String - if data.respond_to?(:lines) - json_pretty_print(data.lines.to_a) - else - json_pretty_print(data.to_a) - end - else - raise ArgumentError, "I can only generate JSON for Hashes, Mashes, Arrays and Strings. You fed me a #{data.class}!" - end - end - - private - - def configure_ohai - Ohai.config.merge!(@config) - - # add any additional CLI passed directories to the plugin path excluding duplicates - unless Ohai.config[:directory].nil? - # make sure the directory config is an array since it could be a string set in client.rb - Array(Ohai.config[:directory]).each do |dir| - next if Ohai.config[:plugin_path].include?(dir) - - Ohai.config[:plugin_path] << dir - end - end - - logger.debug("Running Ohai with the following configuration: #{Ohai.config.configuration}") - end - - def configure_logging - if Ohai.config[:log_level] == :auto - Ohai::Log.level = :info - else - Ohai::Log.level = Ohai.config[:log_level] - end - end - - # Freeze all string values in @data. This makes them immutable and saves - # a bit of RAM. - # - # @api private - # @return [void] - def freeze_strings! - # Recursive visitor pattern helper. - visitor = lambda do |val| - case val - when Hash - val.each_value { |v| visitor.call(v) } - when Array - val.each { |v| visitor.call(v) } - when String - val.freeze - end - end - visitor.call(@data) - end - - # Extract additional backend parameters from Target Mode URL. - # - # @api private - # @return [Hash] - def backend_parameters - uri = URI.parse(config[:target]) - return {} unless uri.query - - CGI.parse(uri.query).map { |k, v| [k.to_sym, v.first] }.to_h - end - end -end +# frozen_string_literal: true +# +# Author:: Adam Jacob (<adam@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_relative "version" +require_relative "loader" +require_relative "log" +require_relative "mash" +require_relative "runner" +require_relative "dsl" +require_relative "mixin/shell_out" +require_relative "mixin/os" +require_relative "mixin/string" +require_relative "mixin/constant_helper" +require_relative "provides_map" +require_relative "hints" +require_relative "config" +require_relative "train_transport" +require "ffi_yajl" unless defined?(FFI_Yajl) +require "cgi" unless defined?(CGI) + +module Ohai + # The class used by Ohai::Application and Chef to actually collect data + class System + include Ohai::Mixin::ConstantHelper + + attr_accessor :data + attr_reader :config + attr_reader :provides_map + attr_reader :logger + attr_writer :transport_connection + + # the cli flag is used to determine if we're being constructed by + # something like chef-client (which doesn't set this flag) and + # which sets up its own loggers, or if we're coming from Ohai::Application + # and therefore need to configure Ohai's own logger. + def initialize(config = {}) + @cli = config[:invoked_from_cli] + @plugin_path = "" + @config = config + @failed_plugins = [] + @logger = config[:logger] || Ohai::Log.with_child + @logger.metadata = { system: "ohai", version: Ohai::VERSION } + reset_system + end + + # clears the current collected data, clears the provides map for plugins, + # refreshes hints, and reconfigures ohai. In short this gets Ohai into a first run state + # + # @return [void] + def reset_system + @data = Mash.new + @provides_map = ProvidesMap.new + + configure_ohai + configure_logging if @cli + + @loader = Ohai::Loader.new(self) + + Ohai::Hints.refresh_hints + + # Remove the previously defined plugins + recursive_remove_constants(Ohai::NamedPlugin) + end + + def runner + @runner ||= + Ohai::Runner.new(self, true).tap do |runner| + runner.transport_connection = transport_connection unless transport_connection.nil? + end + end + + def [](key) + @data[key] + end + + # Resets the system and loads then runs the plugins. This is the primary method called + # to run the system. + # + # @param [Array<String>] attribute_filter the attributes to run. All will be run if not specified + # + # @return [void] + def all_plugins(attribute_filter = nil) + # Reset the system when all_plugins is called since this function + # can be run multiple times in order to pick up any changes in the + # config or plugins with Chef. + reset_system + + load_plugins + run_plugins(true, attribute_filter) + end + + # load all plugins by calling Ohai::Loader.load_all + # + # @see Ohai::Loader.load_all + def load_plugins + @loader.load_all + end + + # get backend parameters for target mode + # + # @return [Train::Transport] + def transport_connection + @transport_connection ||= Ohai::TrainTransport.new(logger).build_transport&.connection + end + + # run all plugins or those that match the attribute filter is provided + # + # @param safe [Boolean] + # @param [Array<String>] attribute_filter the attributes to run. All will be run if not specified + # + # @return [Mash] + def run_plugins(safe = false, attribute_filter = nil) + begin + @provides_map.all_plugins(attribute_filter).each do |plugin| + runner.run_plugin(plugin) + end + transport_connection.close unless transport_connection.nil? + rescue Ohai::Exceptions::AttributeNotFound, Ohai::Exceptions::DependencyCycle => e + logger.error("Encountered error while running plugins: #{e.inspect}") + raise + end + critical_failed = Ohai::Config.ohai[:critical_plugins] & runner.failed_plugins + unless critical_failed.empty? + msg = "The following Ohai plugins marked as critical failed: #{critical_failed}" + if @cli + logger.error(msg) + exit(true) + else + raise Ohai::Exceptions::CriticalPluginFailure, "#{msg}. Failing Chef run." + end + end + + # Freeze all strings. + freeze_strings! + end + + # @param [String] plugin_path + # + # @return [void] + # + def run_additional_plugins(plugin_path) + @loader.load_additional(plugin_path).each do |plugin| + logger.trace "Running plugin #{plugin}" + runner.run_plugin(plugin) + end + + freeze_strings! + end + + # + # Serialize this object as a hash + # + def to_json + FFI_Yajl::Encoder.new.encode(@data) + end + + # + # Pretty Print this object as JSON + # + def json_pretty_print(item = nil) + FFI_Yajl::Encoder.new(pretty: true, validate_utf8: false).encode(item || @data) + end + + def attributes_print(a) + data = @data + a.split("/").each do |part| + data = data[part] + end + raise ArgumentError, "I cannot find an attribute named #{a}!" if data.nil? + + case data + when Hash, Mash, Array, Integer + json_pretty_print(data) + when String + if data.respond_to?(:lines) + json_pretty_print(data.lines.to_a) + else + json_pretty_print(data.to_a) + end + else + raise ArgumentError, "I can only generate JSON for Hashes, Mashes, Arrays and Strings. You fed me a #{data.class}!" + end + end + + private + + def configure_ohai + Ohai.config.merge!(@config) + + # add any additional CLI passed directories to the plugin path excluding duplicates + unless Ohai.config[:directory].nil? + # make sure the directory config is an array since it could be a string set in client.rb + Array(Ohai.config[:directory]).each do |dir| + next if Ohai.config[:plugin_path].include?(dir) + + Ohai.config[:plugin_path] << dir + end + end + + logger.debug("Running Ohai with the following configuration: #{Ohai.config.configuration}") + end + + def configure_logging + if Ohai.config[:log_level] == :auto + Ohai::Log.level = :info + else + Ohai::Log.level = Ohai.config[:log_level] + end + end + + # Freeze all string values in @data. This makes them immutable and saves + # a bit of RAM. + # + # @api private + # @return [void] + def freeze_strings! + # Recursive visitor pattern helper. + visitor = lambda do |val| + case val + when Hash + val.each_value { |v| visitor.call(v) } + when Array + val.each { |v| visitor.call(v) } + when String + val.freeze + end + end + visitor.call(@data) + end + + # Extract additional backend parameters from Target Mode URL. + # + # @api private + # @return [Hash] + def backend_parameters + uri = URI.parse(config[:target]) + return {} unless uri.query + + CGI.parse(uri.query).map { |k, v| [k.to_sym, v.first] }.to_h + end + end +end