Skip to content

Commit

Permalink
Merge pull request #3750 from inspec/cw/cred-set-support-02
Browse files Browse the repository at this point in the history
Formalize Config File
  • Loading branch information
clintoncwolfe committed Feb 6, 2019
2 parents 3cc2ee6 + 6f1c8ab commit 5a5eb4a
Show file tree
Hide file tree
Showing 28 changed files with 1,113 additions and 347 deletions.
1 change: 1 addition & 0 deletions Gemfile
@@ -1,5 +1,6 @@
# encoding: utf-8
source 'https://rubygems.org'

gemspec name: 'inspec'

gem 'ffi', '>= 1.9.14'
Expand Down
7 changes: 7 additions & 0 deletions appveyor.yml
Expand Up @@ -2,6 +2,13 @@ os: Windows Server 2012 R2
platform:
- x64


# Use this to preseve the build VM after the build finishes.
# Note that the appveyor builds will appear to hang; check the build log for details.
# https://www.appveyor.com/docs/how-to/rdp-to-build-worker/
# on_finish:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

environment:
matrix:
- name: unit-tests-ruby-2.3.3
Expand Down
5 changes: 5 additions & 0 deletions etc/deprecations.json
Expand Up @@ -5,6 +5,11 @@
"attrs_value_replaces_default": {
"action": "ignore",
"prefix": "The 'default' option for attributes is being replaced by 'value' - please use it instead."
},
"cli_option_json_config": {
"action": "ignore",
"prefix": "The --json-config option is being replaced by the --config option.",
"comment": "See #3661"
}
}
}
2 changes: 1 addition & 1 deletion inspec-core.gemspec
Expand Up @@ -20,7 +20,7 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = '>= 2.3'

spec.add_dependency 'train-core', '~> 1.5', '= 1.7.1' # 1.7.2 has a regression introduced by train #394
spec.add_dependency 'train-core', '~> 1.5', '>= 1.7.2'
spec.add_dependency 'thor', '~> 0.20'
spec.add_dependency 'json', '>= 1.8', '< 3.0'
spec.add_dependency 'method_source', '~> 0.8'
Expand Down
2 changes: 1 addition & 1 deletion inspec.gemspec
Expand Up @@ -24,7 +24,7 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = '>= 2.3'

spec.add_dependency 'train', '~> 1.5', '= 1.7.1' # 1.7.2 has a regression introduced by train #394
spec.add_dependency 'train', '~> 1.5', '>= 1.7.2'
spec.add_dependency 'thor', '~> 0.20'
spec.add_dependency 'json', '>= 1.8', '< 3.0'
spec.add_dependency 'method_source', '~> 0.8'
Expand Down
2 changes: 1 addition & 1 deletion lib/bundles/inspec-supermarket/cli.rb
Expand Up @@ -30,7 +30,7 @@ def profiles
desc 'exec PROFILE', 'execute a Supermarket profile'
exec_options
def exec(*tests)
o = opts(:exec).dup
o = config
diagnose(o)
configure_logger(o)

Expand Down
14 changes: 7 additions & 7 deletions lib/inspec/backend.rb
Expand Up @@ -41,16 +41,16 @@ def inspect
# @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
Expand Down Expand Up @@ -85,9 +85,9 @@ def self.create(config) # rubocop:disable Metrics/AbcSize

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
182 changes: 12 additions & 170 deletions lib/inspec/base_cli.rb
Expand Up @@ -75,8 +75,9 @@ def self.target_options # rubocop:disable MethodLength
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,
Expand Down Expand Up @@ -118,92 +119,7 @@ def self.exec_options
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
Expand Down Expand Up @@ -286,86 +202,12 @@ def suppress_log_output?(opts)
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
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
def diagnose(_ = nil)
config.diagnose
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
Expand Down Expand Up @@ -409,27 +251,27 @@ def vendor_deps(path, opts)

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
STDOUT
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

0 comments on commit 5a5eb4a

Please sign in to comment.