class Inspec::Runner
rubocop:disable Metrics/ClassLength
“‘
r.run
r.add_target(“url/to/some/profile”)
r.add_target(“/path/to/some/profile”)
r = Inspec::Runner.new()
“`
and then call the run method:
Users are expected to insantiate a runner, add targets to be run,
entry point to the application.
Inspec::Runner coordinates the running of tests and is the main
def add_target(target, _opts = [])
@eturns [Inspec::ProfileContext]
@params _opts [Hash] Unused, but still here to avoid breaking kitchen-inspec
@params target [String] A path or URL to a profile or raw test.
the ProfileContext, the Profile, and Inspec::DSL
TODO: Deduplicate/clarify the loading code that exists in here,
responsible for actually running the tests.
registered with the @test_collector which is ultimately
query the profile for the full list of rules. Those rules are
Once the we've loaded all of the tests files in the profile, we
called using similar code in Inspec::DSL.
loaded on-demand when include_content or required_content are
If the profile depends on other profiles, those profiles will be
into the ProfileContext.
(libraries, tests, and attributes) from the Profile are loaded
target we generate a Profile and a ProfileContext. The content
A target is a path or URL that points to a profile. Using this
run when the user calls the run method.
add_target allows the user to add a target whose tests will be
def add_target(target, _opts = []) profile = Inspec::Profile.for_target(target, cache: @cache, backend: @backend, controls: @controls, attributes: @conf[:attributes]) fail "Could not resolve #{target} to valid input." if profile.nil? @target_profiles << profile if supports_profile?(profile) end
def all_rules
places we read it off of the profile context. To keep the API's
In some places we read the rules off of the runner, in other
def all_rules @rules end
def block_source_info(block)
def block_source_info(block) return {} if block.nil? || !block.respond_to?(:source_location) opts = {} file_path, line = block.source_location opts['file_path'] = file_path opts['line_number'] = line opts end
def configure_transport
def configure_transport @backend = Inspec::Backend.create(@conf) @test_collector.backend = @backend end
def eval_with_virtual_profile(command)
def eval_with_virtual_profile(command) require 'fetchers/mock' add_target({ 'inspec.yml' => 'name: inspec-shell' }) our_profile = @target_profiles.first ctx = our_profile.runner_context ctx.load(command) end
def get_check_example(method_name, arg, block)
def get_check_example(method_name, arg, block) opts = block_source_info(block) if !arg.empty? && arg[0].respond_to?(:resource_skipped) && !arg[0].resource_skipped.nil? return @test_collector.example_group(*arg, opts) do it arg[0].resource_skipped end else # add the resource case method_name when 'describe' return @test_collector.example_group(*arg, opts, &block) when 'expect' return block.example_group when 'describe.one' tests = arg.map do |x| @test_collector.example_group(x[1][0], block_source_info(x[2]), &x[2]) end return nil if tests.empty? ok_tests = tests.find_all(&:run) # return all tests if none succeeds; we will just report full failure return tests if ok_tests.empty? # otherwise return all working tests return ok_tests else fail "A rule was registered with #{method_name.inspect}, "\ "which isn't understood and cannot be processed." end end nil end
def initialize(conf = {})
def initialize(conf = {}) @rules = [] @conf = conf.dup @conf[:logger] ||= Logger.new(nil) @target_profiles = [] @controls = @conf[:controls] || [] @ignore_supports = @conf[:ignore_supports] @create_lockfile = @conf[:create_lockfile] @cache = Inspec::Cache.new(@conf[:cache]) @test_collector = @conf.delete(:test_collector) || begin require 'inspec/runner_rspec' RunnerRspec.new(@conf) end # list of profile attributes @attributes = [] load_attributes(@conf) configure_transport end
def load
def load all_controls = [] @target_profiles.each do |profile| @test_collector.add_profile(profile) write_lockfile(profile) if @create_lockfile profile.locked_dependencies profile.load_libraries @attributes |= profile.runner_context.attributes all_controls += profile.collect_tests end all_controls.each do |rule| register_rule(rule) unless rule.nil? end end
def load_attributes(options)
def load_attributes(options) attributes = {} # read endpoints for secrets eg. yml file secrets_targets = options[:attrs] unless secrets_targets.nil? secrets_targets.each do |target| secrets = Inspec::SecretsBackend.resolve(target) # merge hash values attributes = attributes.merge(secrets.attributes) unless secrets.nil? || secrets.attributes.nil? end end options[:attributes] = options[:attributes] || {} options[:attributes] = options[:attributes].merge(attributes) options[:attributes] end
def register_rule(rule)
def register_rule(rule) Inspec::Log.debug "Registering rule #{rule}" @rules << rule checks = ::Inspec::Rule.prepare_checks(rule) examples = checks.flat_map do |m, a, b| get_check_example(m, a, b) end.compact examples.each { |e| @test_collector.add_test(e, rule) } end
def register_rules(ctx)
def register_rules(ctx) new_tests = false ctx.rules.each do |rule_id, rule| next if block_given? && !(yield rule_id, rule) new_tests = true register_rule(rule) end new_tests end
def reset
def reset @test_collector.reset @target_profiles.each do |profile| profile.runner_context.rules = {} end @rules = [] end
def run(with = nil)
def run(with = nil) Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}" load run_tests(with) end
def run_tests(with = nil)
def run_tests(with = nil) @test_collector.run(with) end
def supports_profile?(profile)
def supports_profile?(profile) return true if @ignore_supports if !profile.supports_runtime? fail 'This profile requires InSpec version '\ "#{profile.metadata.inspec_requirement}. You are running "\ "InSpec v#{Inspec::VERSION}.\n" end if !profile.supports_os? fail "This OS/platform (#{@backend.os[:name]}) is not supported by this profile." end true end
def tests
def tests @test_collector.tests end
def write_lockfile(profile)
def write_lockfile(profile) return false if !profile.writable? if profile.lockfile_exists? Inspec::Log.debug "Using existing lockfile #{profile.lockfile_path}" else Inspec::Log.debug "Creating lockfile: #{profile.lockfile_path}" lockfile = profile.generate_lockfile File.write(profile.lockfile_path, lockfile.to_yaml) end end