lib/inspec/config.rb in inspec-4.3.2 vs lib/inspec/config.rb in inspec-4.6.3
- old
+ new
@@ -1,10 +1,14 @@
# Represents InSpec configuration. Merges defaults, config file options,
# and CLI arguments.
-require 'pp'
-require 'stringio'
+require "pp"
+require "stringio"
+require "forwardable"
+require "thor"
+require "base64"
+require "inspec/base_cli"
module Inspec
class Config
# These are options that apply to any transport
GENERIC_CREDENTIALS = %w{
@@ -25,11 +29,11 @@
def_delegators :@final_options, :each, :delete, :[], :[]=, :key?
attr_reader :final_options
# This makes it easy to make a config with a mock backend.
def self.mock(opts = {})
- Inspec::Config.new({ backend: :mock }.merge(opts), StringIO.new('{}'))
+ Inspec::Config.new({ backend: :mock }.merge(opts), StringIO.new("{}"))
end
# Use this to get a cached version of the config. This prevents you from
# being required to pass it around everywhere.
def self.cached
@@ -56,19 +60,25 @@
def diagnose
return unless self[:diagnose]
puts "InSpec version: #{Inspec::VERSION}"
puts "Train version: #{Train::VERSION}"
- puts 'Command line configuration:'
+ puts "Command line configuration:"
pp @cli_opts
- puts 'JSON configuration file:'
+ puts "JSON configuration file:"
pp @cfg_file_contents
- puts 'Merged configuration:'
+ puts "Merged configuration:"
pp @merged_options
puts
end
+ # return all telemetry options from config
+ # @return [Hash]
+ def telemetry_options
+ final_options.select { |key, _| key.include?("telemetry") }
+ end
+
#-----------------------------------------------------------------------#
# Train Credential Handling
#-----------------------------------------------------------------------#
# Returns a Hash with Symbol keys as follows:
@@ -119,11 +129,11 @@
transport_options.include? option_name # e.g., 'host'
end
credentials.merge!(unprefixed_transport_options)
# If there are any prefixed options, merge them in, stripping the prefix.
- transport_prefix = transport_name.downcase.tr('-', '_') + '_'
+ transport_prefix = transport_name.downcase.tr("-", "_") + "_"
transport_options.each do |bare_option_name|
prefixed_option_name = transport_prefix + bare_option_name.to_s
if final_options.key?(prefixed_option_name)
credentials[bare_option_name.to_s] = final_options[prefixed_option_name]
end
@@ -138,11 +148,11 @@
def _utc_determine_backend(credentials)
return if credentials.key?(:backend)
# Default to local
unless @final_options.key?(:target)
- credentials[:backend] = 'local'
+ credentials[:backend] = "local"
return
end
# Look into target
%r{^(?<transport_name>[a-z_\-0-9]+)://.*$} =~ final_options[:target]
@@ -155,11 +165,11 @@
def _utc_merge_credset(credentials, transport_name)
# Look for Config File credentials/transport_name/credset
credset_name = _utc_find_credset_name(credentials, transport_name)
if credset_name
- credset = @cfg_file_contents.dig('credentials', transport_name, credset_name)
+ credset = @cfg_file_contents.dig("credentials", transport_name, credset_name)
if credset
credentials.merge!(credset)
else
# OK, we had a target that looked like transport://something
# But we don't know what that something is - there was no
@@ -183,11 +193,11 @@
# Reading Config Files
#-----------------------------------------------------------------------#
# Regardless of our situation, end up with a readable IO object
def resolve_cfg_io(cli_opts, cfg_io)
- raise(ArgumentError, 'Inspec::Config must use an IO to read from') if cfg_io && !cfg_io.respond_to?(:read)
+ raise(ArgumentError, "Inspec::Config must use an IO to read from") if cfg_io && !cfg_io.respond_to?(:read)
cfg_io ||= check_for_piped_config(cli_opts)
return cfg_io if cfg_io
path = determine_cfg_path(cli_opts)
@@ -198,23 +208,23 @@
def check_for_piped_config(cli_opts)
cli_opt = cli_opts[:config] || cli_opts[:json_config]
Inspec.deprecate(:cli_option_json_config) if cli_opts.key?(:json_config)
return nil unless cli_opt
- return nil unless cli_opt == '-'
+ return nil unless cli_opt == "-"
# This warning is here so that if a user invokes inspec with --config=-,
# they will have an explanation for why it appears to hang.
- Inspec::Log.warn 'Reading JSON config from standard input' if STDIN.tty?
+ Inspec::Log.warn "Reading JSON config from standard input" if STDIN.tty?
STDIN
end
def determine_cfg_path(cli_opts)
path = cli_opts[:config] || cli_opts[:json_config]
Inspec.deprecate(:cli_option_json_config) if cli_opts.key?(:json_config)
if path.nil?
- default_path = File.join(Inspec.config_dir, 'config.json')
+ default_path = File.join(Inspec.config_dir, "config.json")
path = default_path if File.exist?(default_path)
elsif !File.exist?(path)
raise ArgumentError, "Could not read configuration file at #{path}"
end
path
@@ -237,11 +247,11 @@
end
@cfg_file_contents
end
def file_version
- @cfg_file_contents['version'] || :legacy
+ @cfg_file_contents["version"] || :legacy
end
def legacy_file?
file_version == :legacy
end
@@ -249,30 +259,30 @@
def config_file_cli_options
if legacy_file?
# Assume everything in the file is a CLI option
@cfg_file_contents
else
- @cfg_file_contents['cli_options'] || {}
+ @cfg_file_contents["cli_options"] || {}
end
end
def config_file_reporter_options
# This is assumed to be top-level in both legacy and 1.1.
# Technically, you could sneak it in the 1.1 cli opts area.
- @cfg_file_contents.key?('reporter') ? { 'reporter' => @cfg_file_contents['reporter'] } : {}
+ @cfg_file_contents.key?("reporter") ? { "reporter" => @cfg_file_contents["reporter"] } : {}
end
#-----------------------------------------------------------------------#
# Validation
#-----------------------------------------------------------------------#
def validate_config_file_contents!
- version = @cfg_file_contents['version']
+ version = @cfg_file_contents["version"]
# Assume legacy format, which is unconstrained
return unless version
- unless version == '1.1'
+ unless version == "1.1"
raise Inspec::ConfigError::Invalid, "Unsupported config file version '#{version}' - currently supported versions: 1.1"
end
valid_fields = %w{version cli_options credentials compliance reporter}.sort
@cfg_file_contents.keys.each do |seen_field|
@@ -284,39 +294,39 @@
def validate_reporters!(reporters)
return if reporters.nil?
# TODO: move this into a reporter plugin type system
valid_types = [
- 'automate',
- 'cli',
- 'documentation',
- 'html',
- 'json',
- 'json-automate',
- 'json-min',
- 'json-rspec',
- 'junit',
- 'progress',
- 'yaml',
+ "automate",
+ "cli",
+ "documentation",
+ "html",
+ "json",
+ "json-automate",
+ "json-min",
+ "json-rspec",
+ "junit",
+ "progress",
+ "yaml",
]
reporters.each do |reporter_name, reporter_config|
raise NotImplementedError, "'#{reporter_name}' is not a valid reporter type." unless valid_types.include?(reporter_name)
- next unless reporter_name == 'automate'
+ next unless reporter_name == "automate"
%w{token url}.each do |option|
raise Inspec::ReporterError, "You must specify a automate #{option} via the config file." if reporter_config[option].nil?
end
end
# check to make sure we are only reporting one type to stdout
stdout_reporters = 0
reporters.each_value do |reporter_config|
- stdout_reporters += 1 if reporter_config['stdout'] == true
+ stdout_reporters += 1 if reporter_config["stdout"] == true
end
- raise ArgumentError, 'The option --reporter can only have a single report outputting to stdout.' if stdout_reporters > 1
+ raise ArgumentError, "The option --reporter can only have a single report outputting to stdout." if stdout_reporters > 1
end
#-----------------------------------------------------------------------#
# Merging Options
#-----------------------------------------------------------------------#
@@ -356,81 +366,81 @@
Inspec::BaseCLI.inspec_cli_command = @command_name # TODO: move to a more relevant location
end
def finalize_parse_reporters(options) # rubocop:disable Metrics/AbcSize
# default to cli report for ad-hoc runners
- options['reporter'] = ['cli'] if options['reporter'].nil?
+ options["reporter"] = ["cli"] if options["reporter"].nil?
# parse out cli to proper report format
- if options['reporter'].is_a?(Array)
+ if options["reporter"].is_a?(Array)
reports = {}
- options['reporter'].each do |report|
- reporter_name, destination = report.split(':', 2)
- if destination.nil? || destination.strip == '-'
- reports[reporter_name] = { 'stdout' => true }
+ options["reporter"].each do |report|
+ reporter_name, destination = report.split(":", 2)
+ if destination.nil? || destination.strip == "-"
+ reports[reporter_name] = { "stdout" => true }
else
reports[reporter_name] = {
- 'file' => destination,
- 'stdout' => false,
+ "file" => destination,
+ "stdout" => false,
}
- reports[reporter_name]['target_id'] = options['target_id'] if options['target_id']
+ reports[reporter_name]["target_id"] = options["target_id"] if options["target_id"]
end
end
- options['reporter'] = reports
+ options["reporter"] = reports
end
# add in stdout if not specified
- if options['reporter'].is_a?(Hash)
- options['reporter'].each do |reporter_name, config|
- options['reporter'][reporter_name] = {} if config.nil?
- options['reporter'][reporter_name]['stdout'] = true if options['reporter'][reporter_name].empty?
- options['reporter'][reporter_name]['target_id'] = options['target_id'] if options['target_id']
+ if options["reporter"].is_a?(Hash)
+ options["reporter"].each do |reporter_name, config|
+ options["reporter"][reporter_name] = {} if config.nil?
+ options["reporter"][reporter_name]["stdout"] = true if options["reporter"][reporter_name].empty?
+ options["reporter"][reporter_name]["target_id"] = options["target_id"] if options["target_id"]
end
end
- validate_reporters!(options['reporter'])
+ validate_reporters!(options["reporter"])
options
end
def finalize_handle_sudo(options)
# Due to limitations in Thor it is not possible to set an argument to be
# both optional and its value to be mandatory. E.g. the user supplying
# the --password argument is optional and not always required, but
# whenever it is used, it requires a value. Handle options that were
# defined in such a way and require a value here:
%w{password sudo-password}.each do |option_name|
- snake_case_option_name = option_name.tr('-', '_').to_s
+ snake_case_option_name = option_name.tr("-", "_").to_s
next unless options[snake_case_option_name] == -1 # Thor sets -1 for missing value - see #1918
raise ArgumentError, "Please provide a value for --#{option_name}. For example: --#{option_name}=hello."
end
# Infer `--sudo` if using `--sudo-password` without `--sudo`
- if options['sudo_password'] && !options['sudo']
- options['sudo'] = true
- Inspec::Log.warn '`--sudo-password` used without `--sudo`. Adding `--sudo`.'
+ if options["sudo_password"] && !options["sudo"]
+ options["sudo"] = true
+ Inspec::Log.warn "`--sudo-password` used without `--sudo`. Adding `--sudo`."
end
end
def finalize_compliance_login(options)
# check for compliance settings
# This is always a hash, comes from config file, not CLI opts
- if options.key?('compliance')
- require 'plugins/inspec-compliance/lib/inspec-compliance/api'
- InspecPlugins::Compliance::API.login(options['compliance'])
+ if options.key?("compliance")
+ require "plugins/inspec-compliance/lib/inspec-compliance/api"
+ InspecPlugins::Compliance::API.login(options["compliance"])
end
end
class Defaults
DEFAULTS = {
exec: {
- 'reporter' => ['cli'],
- 'show_progress' => false,
- 'color' => true,
- 'create_lockfile' => true,
- 'backend_cache' => true,
+ "reporter" => ["cli"],
+ "show_progress" => false,
+ "color" => true,
+ "create_lockfile" => true,
+ "backend_cache" => true,
},
shell: {
- 'reporter' => ['cli'],
+ "reporter" => ["cli"],
},
}.freeze
def self.for_command(command_name)
DEFAULTS[command_name] || {}