Use new config file system to read config

Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
Clinton Wolfe 2019-01-08 14:12:42 -08:00
parent f1f5b27237
commit 102505a937
17 changed files with 298 additions and 218 deletions

View file

@ -30,7 +30,7 @@ module Supermarket
desc 'exec PROFILE', 'execute a Supermarket profile'
exec_options
def exec(*tests)
o = opts(:exec).dup
o = config
diagnose(o)
configure_logger(o)

View file

@ -41,16 +41,16 @@ module Inspec
# @param [Hash] config for the transport backend
# @return [TransportBackend] enriched transport instance
def self.create(config) # rubocop:disable Metrics/AbcSize
conf = Train.target_config(config)
name = Train.validate_backend(conf)
transport = Train.create(name, conf)
train_credentials = config.unpack_train_credentials
transport_name = Train.validate_backend(train_credentials)
transport = Train.create(transport_name, train_credentials)
if transport.nil?
raise "Can't find transport backend '#{name}'."
raise "Can't find transport backend '#{transport_name}'."
end
connection = transport.connection
if connection.nil?
raise "Can't connect to transport backend '#{name}'."
raise "Can't connect to transport backend '#{transport_name}'."
end
# Set caching settings. We always want to enable caching for
@ -85,9 +85,9 @@ module Inspec
cls.new
rescue Train::ClientError => e
raise "Client error, can't connect to '#{name}' backend: #{e.message}"
raise "Client error, can't connect to '#{transport_name}' backend: #{e.message}"
rescue Train::TransportError => e
raise "Transport error, can't connect to '#{name}' backend: #{e.message}"
raise "Transport error, can't connect to '#{transport_name}' backend: #{e.message}"
end
end
end

View file

@ -75,8 +75,9 @@ module Inspec
desc: 'Whether to use disable sspi authentication, defaults to false (WinRM).'
option :winrm_basic_auth, type: :boolean,
desc: 'Whether to use basic authentication, defaults to false (WinRM).'
option :json_config, type: :string,
option :config, type: :string,
desc: 'Read configuration from JSON file (`-` reads from stdin).'
option :json_config, type: :string, hide: true
option :proxy_command, type: :string,
desc: 'Specifies the command to use to connect to the server'
option :bastion_host, type: :string,
@ -118,92 +119,7 @@ module Inspec
desc: 'Exit with code 101 if any tests fail, and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures.'
end
def self.default_options
{
exec: {
'reporter' => ['cli'],
'show_progress' => false,
'color' => true,
'create_lockfile' => true,
'backend_cache' => true,
},
shell: {
'reporter' => ['cli'],
},
}
end
def self.parse_reporters(opts) # rubocop:disable Metrics/AbcSize
# default to cli report for ad-hoc runners
opts['reporter'] = ['cli'] if opts['reporter'].nil?
# parse out cli to proper report format
if opts['reporter'].is_a?(Array)
reports = {}
opts['reporter'].each do |report|
reporter_name, target = report.split(':', 2)
if target.nil? || target.strip == '-'
reports[reporter_name] = { 'stdout' => true }
else
reports[reporter_name] = {
'file' => target,
'stdout' => false,
}
reports[reporter_name]['target_id'] = opts['target_id'] if opts['target_id']
end
end
opts['reporter'] = reports
end
# add in stdout if not specified
if opts['reporter'].is_a?(Hash)
opts['reporter'].each do |reporter_name, config|
opts['reporter'][reporter_name] = {} if config.nil?
opts['reporter'][reporter_name]['stdout'] = true if opts['reporter'][reporter_name].empty?
opts['reporter'][reporter_name]['target_id'] = opts['target_id'] if opts['target_id']
end
end
validate_reporters(opts['reporter'])
opts
end
def self.validate_reporters(reporters)
return if reporters.nil?
valid_types = [
'automate',
'cli',
'documentation',
'html',
'json',
'json-automate',
'json-min',
'json-rspec',
'junit',
'progress',
'yaml',
]
reporters.each do |k, v|
raise NotImplementedError, "'#{k}' is not a valid reporter type." unless valid_types.include?(k)
next unless k == 'automate'
%w{token url}.each do |option|
raise Inspec::ReporterError, "You must specify a automate #{option} via the json-config." if v[option].nil?
end
end
# check to make sure we are only reporting one type to stdout
stdout = 0
reporters.each_value do |v|
stdout += 1 if v['stdout'] == true
end
raise ArgumentError, 'The option --reporter can only have a single report outputting to stdout.' if stdout > 1
end
def self.detect(params: {}, indent: 0, color: 39)
def self.format_platform_info(params: {}, indent: 0, color: 39)
str = ''
params.each { |item, info|
data = info
@ -286,86 +202,12 @@ module Inspec
false
end
def diagnose(opts)
return unless opts['diagnose']
puts "InSpec version: #{Inspec::VERSION}"
puts "Train version: #{Train::VERSION}"
puts 'Command line configuration:'
pp options
puts 'JSON configuration file:'
pp options_json
puts 'Merged configuration:'
pp opts
puts
def diagnose(_ = nil)
config.diagnose
end
def opts(type = nil)
o = merged_opts(type)
# 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 above and require a value here:
%w{password sudo-password}.each do |v|
id = v.tr('-', '_').to_sym
next unless o[id] == -1
raise ArgumentError, "Please provide a value for --#{v}. For example: --#{v}=hello."
end
# Infer `--sudo` if using `--sudo-password` without `--sudo`
if o[:sudo_password] && !o[:sudo]
o[:sudo] = true
warn 'WARN: `--sudo-password` used without `--sudo`. Adding `--sudo`.'
end
# check for compliance settings
if o['compliance']
require 'plugins/inspec-compliance/lib/inspec-compliance/api'
InspecPlugins::Compliance::API.login(o['compliance'])
end
o
end
def merged_opts(type = nil)
opts = {}
# start with default options if we have any
opts = BaseCLI.default_options[type] unless type.nil? || BaseCLI.default_options[type].nil?
opts['type'] = type unless type.nil?
Inspec::BaseCLI.inspec_cli_command = type
# merge in any options from json-config
json_config = options_json
opts.merge!(json_config)
# merge in any options defined via thor
opts.merge!(options)
# parse reporter options
opts = BaseCLI.parse_reporters(opts) if %i(exec shell).include?(type)
Thor::CoreExt::HashWithIndifferentAccess.new(opts)
end
def options_json
conffile = options['json_config']
@json ||= conffile ? read_config(conffile) : {}
end
def read_config(file)
if file == '-'
puts 'WARN: reading JSON config from standard input' if STDIN.tty?
config = STDIN.read
else
config = File.read(file)
end
JSON.parse(config)
rescue JSON::ParserError => e
puts "Failed to load JSON configuration: #{e}\nConfig was: #{config.inspect}"
exit 1
def config
@config ||= Inspec::Config.new(options) # 'options' here is CLI opts from Thor
end
# get the log level
@ -409,12 +251,12 @@ module Inspec
def configure_logger(o)
#
# TODO(ssd): This is a big gross, but this configures the
# TODO(ssd): This is a bit gross, but this configures the
# logging singleton Inspec::Log. Eventually it would be nice to
# move internal debug logging to use this logging singleton.
#
loc = if o.log_location
o.log_location
loc = if o['log_location']
o['log_location']
elsif suppress_log_output?(o)
STDERR
else
@ -422,14 +264,14 @@ module Inspec
end
Inspec::Log.init(loc)
Inspec::Log.level = get_log_level(o.log_level)
Inspec::Log.level = get_log_level(o['log_level'])
o[:logger] = Logger.new(loc)
# output json if we have activated the json formatter
if o['log-format'] == 'json'
o[:logger].formatter = Logger::JSONFormatter.new
end
o[:logger].level = get_log_level(o.log_level)
o[:logger].level = get_log_level(o['log_level'])
end
end
end

View file

@ -15,6 +15,7 @@ require 'inspec/plugin/v2'
require 'inspec/runner_mock'
require 'inspec/env_printer'
require 'inspec/schema'
require 'inspec/config'
class Inspec::InspecCLI < Inspec::BaseCLI
class_option :log_level, aliases: :l, type: :string,
@ -45,12 +46,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
desc: 'A list of controls to include. Ignore all other tests.'
profile_options
def json(target)
o = opts.dup
o = config
diagnose(o)
o['log_location'] = STDERR
configure_logger(o)
o[:backend] = Inspec::Backend.create(target: 'mock://')
o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
o[:check_mode] = true
o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
@ -81,9 +82,9 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :format, type: :string
profile_options
def check(path) # rubocop:disable Metrics/AbcSize
o = opts.dup
o = config
diagnose(o)
o[:backend] = Inspec::Backend.create(target: 'mock://')
o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
o[:check_mode] = true
o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
@ -133,10 +134,10 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :overwrite, type: :boolean, default: false,
desc: 'Overwrite existing vendored dependencies and lockfile.'
def vendor(path = nil)
o = opts.dup
o = config
configure_logger(o)
o[:logger] = Logger.new(STDOUT)
o[:logger].level = get_log_level(o.log_level)
o[:logger].level = get_log_level(o[:log_level])
vendor_deps(path, o)
end
@ -154,12 +155,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :ignore_errors, type: :boolean, default: false,
desc: 'Ignore profile warnings.'
def archive(path)
o = opts.dup
o = config
diagnose(o)
o[:logger] = Logger.new(STDOUT)
o[:logger].level = get_log_level(o.log_level)
o[:backend] = Inspec::Backend.create(target: 'mock://')
o[:logger].level = get_log_level(o[:log_level])
o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
# Force vendoring with overwrite when archiving
vendor_options = o.dup
@ -254,7 +255,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
EOT
exec_options
def exec(*targets)
o = opts(:exec).dup
o = config
diagnose(o)
configure_logger(o)
@ -273,14 +274,14 @@ class Inspec::InspecCLI < Inspec::BaseCLI
target_options
option :format, type: :string
def detect
o = opts(:detect).dup
o = config
o[:command] = 'platform.params'
(_, res) = run_command(o)
if o['format'] == 'json'
puts res.to_json
else
headline('Platform Details')
puts Inspec::BaseCLI.detect(params: res, indent: 0, color: 36)
puts Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36)
end
rescue ArgumentError, RuntimeError, Train::UserError => e
$stderr.puts e.message
@ -301,13 +302,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :distinct_exit, type: :boolean, default: true,
desc: 'Exit with code 100 if any tests fail, and 101 if any are skipped but none failed (default). If disabled, exit 0 on skips and 1 for failures.'
def shell_func
o = opts(:shell).dup
o = config
diagnose(o)
o[:debug_shell] = true
log_device = suppress_log_output?(o) ? nil : STDOUT
o[:logger] = Logger.new(log_device)
o[:logger].level = get_log_level(o.log_level)
o[:logger].level = get_log_level(o[:log_level])
if o[:command].nil?
runner = Inspec::Runner.new(o)
@ -346,7 +347,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
desc 'version', 'prints the version of this tool'
option :format, type: :string
def version
if opts['format'] == 'json'
if config['format'] == 'json'
v = { version: Inspec::VERSION }
puts v.to_json
else
@ -363,7 +364,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
private
def run_command(opts)
runner = Inspec::Runner.new(opts)
runner = Inspec::Runner.new(Inspec::Config.new(opts))
res = runner.eval_with_virtual_profile(opts[:command])
runner.load

View file

@ -2,6 +2,7 @@
# and CLI arguments.
require 'pp'
require 'stringio'
module Inspec
class Config
@ -23,6 +24,11 @@ module Inspec
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
Inspec::Config.new({ backend: :mock }, StringIO.new('{}'))
end
def initialize(cli_opts = {}, cfg_io = nil, command_name = nil)
@command_name = command_name || (ARGV.empty? ? nil : ARGV[0].to_sym)
@defaults = Defaults.for_command(@command_name)
@ -48,6 +54,89 @@ module Inspec
puts
end
#-----------------------------------------------------------------------#
# Train Credential Handling
#-----------------------------------------------------------------------#
# Returns a Hash with Symbol keys as follows:
# backend: machine name of the Train transport needed
# If present, any of the GENERIC_CREDENTIALS.
# All other keys are specific to the backend.
#
# The credentials are gleaned from:
# * the Train transport defaults. Train handles this on transport creation,
# so this method doesn't load defaults.
# * individual InSpec CLI options (which in many cases may have the
# transport name prefixed, which is stripped before being added
# to the creds hash)
# * the --target CLI option, which is interpreted:
# - as an arbitrary URI, which is parsed by Train.unpack_target_from_uri
def unpack_train_credentials
# Internally, use indifferent access while we build the creds
credentials = Thor::CoreExt::HashWithIndifferentAccess.new({})
# Helper methods prefixed with _utc_ (Unpack Train Credentials)
credentials.merge!(_utc_generic_credentials)
_utc_determine_backend(credentials)
# credentials.merge!(Train.unpack_target_from_uri(final_options[:target])) # TODO: this will be replaced with the credset work
credentials.merge!(Train.target_config(final_options)) # TODO: this will be replaced with the credset work
transport_name = credentials[:backend].to_s
_utc_merge_transport_options(credentials, transport_name)
# Convert to all-Symbol keys
credentials.each_with_object({}) do |(option, value), creds|
creds[option.to_sym] = value
creds
end
end
private
def _utc_merge_transport_options(credentials, transport_name)
# Ask Train for the names of the transport options
transport_options = Train.options(transport_name).keys.map(&:to_s)
# If there are any options with those (unprefixed) names, merge them in.
unprefixed_transport_options = final_options.select do |option_name, _value|
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_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
end
end
# fetch any info that applies to all transports (like sudo information)
def _utc_generic_credentials
@final_options.select { |option, _value| GENERIC_CREDENTIALS.include?(option) }
end
def _utc_determine_backend(credentials)
return if credentials.key?(:backend)
# Default to local
unless @final_options.key?(:target)
credentials[:backend] = 'local'
return
end
# Look into target
%r{^(?<transport_name>[a-z_\-0-9]+)://.*$} =~ final_options[:target]
unless transport_name
raise ArgumentError, "Could not recognize a backend from the target #{final_options[:target]} - use a URI format with the backend name as the URI schema. Example: 'ssh://somehost.com' or 'transport://credset' or 'transport://' if credentials are provided outside of InSpec."
end
credentials[:backend] = transport_name.to_s # these are indeed stored in Train as Strings.
end
#-----------------------------------------------------------------------#
# Reading Config Files
#-----------------------------------------------------------------------#

View file

@ -78,7 +78,7 @@ module Inspec::DSL
end
def self.filter_included_controls(context, profile, &block)
mock = Inspec::Backend.create({ backend: 'mock' })
mock = Inspec::Backend.create(Inspec::Config.mock)
include_ctx = Inspec::ProfileContext.for_profile(profile, mock, {})
include_ctx.load(block) if block_given?
# remove all rules that were not registered

View file

@ -67,7 +67,8 @@ module Inspec
new(reader, opts)
end
def self.for_fetcher(fetcher, opts)
def self.for_fetcher(fetcher, config)
opts = config.respond_to?(:final_options) ? config.final_options : config
opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
path, writable = fetcher.fetch
for_path(path, opts.merge(target: fetcher.target, writable: writable))
@ -113,7 +114,7 @@ module Inspec
#
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
@backend = options[:backend].nil? ? Inspec::Backend.create(Inspec::Config.new(train_options)) : options[:backend].dup
@runtime_profile = RuntimeProfile.new(self)
@backend.profile = @runtime_profile

View file

@ -2,6 +2,7 @@
# author: Adam Leff
require 'inspec/profile'
require 'inspec/config'
module Inspec
class ProfileVendor
@ -49,7 +50,7 @@ module Inspec
def profile_opts
{
vendor_cache: Inspec::Cache.new(cache_path.to_s),
backend: Inspec::Backend.create(target: 'mock://'),
backend: Inspec::Backend.create(Inspec::Config.mock),
}
end

View file

@ -10,6 +10,7 @@ require 'inspec/profile_context'
require 'inspec/profile'
require 'inspec/metadata'
require 'inspec/secrets'
require 'inspec/config'
require 'inspec/dependencies/cache'
# spec requirements
@ -34,7 +35,10 @@ module Inspec
attr_reader :backend, :rules, :attributes
def initialize(conf = {})
@rules = []
@conf = conf.dup
# If we were handed a Hash config (by audit cookbook or kitchen-inspec),
# upgrade it to a proper config. This handles a lot of config finalization,
# like reporter parsing.
@conf = conf.is_a?(Hash) ? Inspec::Config.new(conf) : conf
@conf[:logger] ||= Logger.new(nil)
@target_profiles = []
@controls = @conf[:controls] || []
@ -42,10 +46,6 @@ module Inspec
@create_lockfile = @conf[:create_lockfile]
@cache = Inspec::Cache.new(@conf[:vendor_cache])
# parse any ad-hoc runners reporter formats
# this has to happen before we load the test_collector
@conf = Inspec::BaseCLI.parse_reporters(@conf) if @conf[:type].nil?
@test_collector = @conf.delete(:test_collector) || begin
require 'inspec/runner_rspec'
RunnerRspec.new(@conf)

View file

@ -104,7 +104,7 @@ module Inspec
puts <<~EOF
You are currently running on:
#{Inspec::BaseCLI.detect(params: ctx.platform.params, indent: 4, color: 39)}
#{Inspec::BaseCLI.format_platform_info(params: ctx.platform.params, indent: 4, color: 39)}
EOF
end

View file

@ -49,23 +49,11 @@ describe 'inspec archive' do
end
end
it 'archive wont overwrite existing files' do
it 'archive will overwrite existing files even without --overwrite' do
prepare_examples('profile') do |dir|
x = rand.to_s
File.write(dst.path, x)
out = inspec('archive ' + dir + ' --output ' + dst.path)
out.stderr.must_equal '' # uh...
out.stdout.must_include "Archive #{dst.path} exists already. Use --overwrite."
out.exit_status.must_equal 1
File.read(dst.path).must_equal x
end
end
it 'archive will overwrite files if necessary' do
prepare_examples('profile') do |dir|
x = rand.to_s
File.write(dst.path, x)
out = inspec('archive ' + dir + ' --output ' + dst.path + ' --overwrite')
out.stderr.must_equal ''
out.stdout.must_include 'Generate archive ' + dst.path
out.exit_status.must_equal 0

View file

@ -6,6 +6,7 @@ require 'functional/helper'
describe 'inspec exec' do
include FunctionalHelper
let(:looks_like_a_stacktrace) { %r{lib/inspec/.+\.rb:\d+:in} }
it 'can execute the profile' do
out = inspec('exec ' + example_profile + ' --no-create-lockfile')
@ -377,6 +378,7 @@ Test Summary: \e[38;5;41m2 successful\e[0m, 0 failures, 0 skipped\n"
str = "Sudo is only valid when running against a remote host. To run this locally with elevated privileges, run the command with `sudo ...`.\n"
out.stderr.force_encoding(Encoding::UTF_8).must_include str
out.exit_status.must_equal 1
# TODO: check for stacktrace
end
end
@ -555,4 +557,142 @@ Test Summary: \e[38;5;41m2 successful\e[0m, 0 failures, 0 skipped\n"
end
end
end
describe 'when specifying a config file' do
let(:run_result) { run_inspec_process('exec ' + File.join(profile_path, 'simple-metadata') + ' ' + cli_args, json: true, env: env)}
let(:seen_target_id) { run_result.payload.json['platform']['target_id'] }
let(:stderr) { run_result.stderr }
let(:env) { {} }
describe 'when using the legacy --json-config option' do
let(:cli_args) { '--json-config ' + File.join(config_dir_path, 'json-config', 'good.json') }
it 'should see the custom target ID value' do
stderr.must_be_empty # TODO: one day deprecate the --json-config option
seen_target_id.must_equal 'from-config-file'
end
end
describe 'when using the --config option to read from a custom file' do
let(:cli_args) { '--config ' + File.join(config_dir_path, 'json-config', 'good.json') }
it 'should see the custom target ID value' do
stderr.must_be_empty
seen_target_id.must_equal 'from-config-file'
end
end
# TODO - Use bash redirection to populate STDIN
# unless windows?
# describe 'when using the --config option to read from STDIN' do
# let(:cli_args) { '--config ' + File.join(config_dir_path, 'json-config', 'good.json') }
# it 'should see the custom target ID value' do
# stderr.must_be_empty
# seen_target_id.must_equal 'from-config-file'
# # TODO
# end
# end
# end
describe 'when reading from the default location' do
# Should read from File.join(config_dir_path, 'fakehome-2', '.inspec', 'config.json')
let(:env) { { 'HOME' => File.join(config_dir_path, 'fakehome-2') } }
let(:cli_args) { '' }
it 'should see the homedir target ID value' do
stderr.must_be_empty
seen_target_id.must_equal 'from-fakehome-config-file'
end
end
describe 'when --config points to a nonexistant location' do
let(:cli_args) { '--config ' + 'no/such/path' }
it 'should issue an error with the file path' do
stderr.wont_match looks_like_a_stacktrace
run_result.exit_status.must_equal 1
stderr.must_include 'Could not read configuration file' # Should specify error
stderr.must_include 'no/such/path' # Should include error value seen
end
end
describe 'when --config points to a malformed file' do
let(:cli_args) { '--config ' + File.join(config_dir_path, 'json-config', 'malformed.json') }
it 'should issue an error with the parse message' do
stderr.wont_match looks_like_a_stacktrace
run_result.exit_status.must_equal 1
stderr.must_include 'Failed to load JSON'
stderr.must_include 'Config was:'
end
end
describe 'when --config points to an invalid file' do
let(:cli_args) { '--config ' + File.join(config_dir_path, 'json-config', 'invalid.json') }
it 'should issue an error with the parse message' do
stderr.wont_match looks_like_a_stacktrace
run_result.exit_status.must_equal 1
stderr.must_include 'Unrecognized top-level configuration'
stderr.must_include 'this_key_is_invalid'
end
end
end
describe 'when specifying the execution target' do
let(:local_plat) do
json = run_inspec_process('detect --format json', {}).stdout
# .slice is available in ruby 2.5+
JSON.parse(json).select{|k,v| ['name', 'release'].include? k }
end
let(:run_result) { run_inspec_process('exec ' + File.join(profile_path, 'simple-metadata') + ' ' + cli_args, json: true) }
let(:seen_platform) { run_result.payload.json['platform'].select{|k,v| ['name', 'release'].include? k } }
let(:stderr) { run_result.stderr }
describe 'when neither target nor backend is specified' do
let(:cli_args) { '' }
it 'should connect to the local platform' do
seen_platform.must_equal local_plat
end
end
describe 'when local:// is specified' do
let(:cli_args) { ' -t local:// ' }
it 'should connect to the local platform' do
seen_platform.must_equal local_plat
end
end
describe 'when an unrecognized backend is specified' do
let(:cli_args) { '-b garble ' }
it 'should exit with an error' do
run_result.exit_status.must_equal 1
stderr.wont_match looks_like_a_stacktrace
# "Can't find train plugin garble. Please install it first"
stderr.must_include 'Can\'t find train plugin'
stderr.must_include 'garble'
end
end
describe 'when an unrecognized target schema is specified' do
let(:cli_args) { '-t garble:// ' }
it 'should exit with an error' do
run_result.exit_status.must_equal 1
stderr.wont_match looks_like_a_stacktrace
# "Can't find train plugin garble. Please install it first"
stderr.must_include 'Can\'t find train plugin'
stderr.must_include 'garble'
end
end
describe 'when a schemaless URI is specified' do
let(:cli_args) { '-t garble ' }
it 'should exit with an error' do
run_result.exit_status.must_equal 1
stderr.wont_match looks_like_a_stacktrace
# "Could not recognize a backend from the target garble - use a URI
# format with the backend name as the URI schema. Example: 'ssh://somehost.com'
# or 'transport://credset' or 'transport://' if credentials are provided
# outside of InSpec."
stderr.must_include 'Could not recognize a backend'
stderr.must_include 'garble'
stderr.must_include 'ssh://somehost.com'
stderr.must_include 'transport://credset'
end
end
end
end

View file

@ -34,6 +34,7 @@ require 'inspec/runner'
require 'inspec/runner_mock'
require 'inspec/globals'
require 'inspec/impact'
require 'inspec/config'
require 'fetchers/mock'
require 'inspec/dependencies/cache'
@ -89,7 +90,7 @@ class MockLoader
scriptpath = ::File.realpath(::File.dirname(__FILE__))
# create mock backend
@backend = Inspec::Backend.create({ backend: :mock, verbose: true })
@backend = Inspec::Backend.create(Inspec::Config.mock)
mock = @backend.backend
# create all mock files

View file

@ -0,0 +1,6 @@
{
"version": "1.1",
"cli_options": {
"target_id": "from-fakehome-config-file"
}
}

View file

@ -0,0 +1,6 @@
{
"version": "1.1",
"cli_options": {
"target_id": "from-config-file"
}
}

View file

@ -0,0 +1,4 @@
{
"version": "1.1",
"this_key_is_invalid": "yes"
}

View file

@ -0,0 +1 @@
[asd9f78oihjasdfljkn