mirror of
https://github.com/inspec/inspec
synced 2024-11-23 13:13:22 +00:00
Merge pull request #994 from chef/ssd/resource-isolation
Initial attempt at isolating resources between dependencies
This commit is contained in:
commit
90a8584d10
25 changed files with 573 additions and 391 deletions
|
@ -70,7 +70,7 @@ module Inspec
|
|||
o[:logger].level = get_log_level(o.log_level)
|
||||
|
||||
runner = Inspec::Runner.new(o)
|
||||
targets.each { |target| runner.add_target(target, opts) }
|
||||
targets.each { |target| runner.add_target(target) }
|
||||
exit runner.run
|
||||
rescue RuntimeError, Train::UserError => e
|
||||
$stderr.puts e.message
|
||||
|
|
145
lib/inspec/control_eval_context.rb
Normal file
145
lib/inspec/control_eval_context.rb
Normal file
|
@ -0,0 +1,145 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
require 'inspec/dsl'
|
||||
require 'inspec/dsl_shared'
|
||||
|
||||
module Inspec
|
||||
#
|
||||
# ControlEvalContext constructs an anonymous class that control
|
||||
# files will be instance_exec'd against.
|
||||
#
|
||||
# The anonymous class includes the given passed resource_dsl as well
|
||||
# as the basic DSL of the control files (describe, control, title,
|
||||
# etc).
|
||||
#
|
||||
class ControlEvalContext
|
||||
# Create the context for controls. This includes all components of the DSL,
|
||||
# including matchers and resources.
|
||||
#
|
||||
# @param [ResourcesDSL] resources_dsl which has all resources to attach
|
||||
# @return [RuleContext] the inner context of rules
|
||||
def self.rule_context(resources_dsl)
|
||||
require 'rspec/core/dsl'
|
||||
Class.new(Inspec::Rule) do
|
||||
include RSpec::Core::DSL
|
||||
include resources_dsl
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the heart of the control eval context:
|
||||
#
|
||||
# An instantiated object which has all resources registered to it
|
||||
# and exposes them to the a test file.
|
||||
#
|
||||
# @param profile_context [Inspec::ProfileContext]
|
||||
# @param outer_dsl [OuterDSLClass]
|
||||
# @return [ProfileContextClass]
|
||||
#
|
||||
# rubocop:disable Lint/NestedMethodDefinition
|
||||
def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
||||
rule_class = rule_context(resources_dsl)
|
||||
profile_context_owner = profile_context
|
||||
profile_id = profile_context.profile_id
|
||||
|
||||
Class.new do
|
||||
include Inspec::DSL
|
||||
include Inspec::DSL::RequireOverride
|
||||
include resources_dsl
|
||||
|
||||
def initialize(backend, conf, dependencies, require_loader)
|
||||
@backend = backend
|
||||
@conf = conf
|
||||
@dependencies = dependencies
|
||||
@require_loader = require_loader
|
||||
@skip_profile = false
|
||||
end
|
||||
|
||||
define_method :title do |arg|
|
||||
profile_context_owner.set_header(:title, arg)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Control Evaluation Context (#{profile_name})"
|
||||
end
|
||||
|
||||
define_method :profile_name do
|
||||
profile_id
|
||||
end
|
||||
|
||||
define_method :control do |*args, &block|
|
||||
id = args[0]
|
||||
opts = args[1] || {}
|
||||
register_control(rule_class.new(id, profile_id, opts, &block))
|
||||
end
|
||||
|
||||
#
|
||||
# Describe allows users to write rspec-like bare describe
|
||||
# blocks without declaring an inclosing control. Here, we
|
||||
# generate a control for them automatically and then execute
|
||||
# the describe block in the context of that control.
|
||||
#
|
||||
define_method :describe do |*args, &block|
|
||||
loc = block_location(block, caller[0])
|
||||
id = "(generated from #{loc} #{SecureRandom.hex})"
|
||||
|
||||
res = nil
|
||||
rule = rule_class.new(id, profile_id, {}) do
|
||||
res = describe(*args, &block)
|
||||
end
|
||||
register_control(rule, &block)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
define_method :add_resources do |context|
|
||||
self.class.class_eval do
|
||||
include context.to_resources_dsl
|
||||
end
|
||||
|
||||
rule_class.class_eval do
|
||||
include context.to_resources_dsl
|
||||
end
|
||||
end
|
||||
|
||||
define_method :add_subcontext do |context|
|
||||
profile_context_owner.add_subcontext(context)
|
||||
end
|
||||
|
||||
define_method :register_control do |control, &block|
|
||||
::Inspec::Rule.set_skip_rule(control, true) if @skip_profile
|
||||
|
||||
profile_context_owner.register_rule(control, &block) unless control.nil?
|
||||
end
|
||||
|
||||
# method for attributes; import attribute handling
|
||||
define_method :attribute do |name, options|
|
||||
profile_context_owner.register_attribute(name, options)
|
||||
end
|
||||
|
||||
define_method :skip_control do |id|
|
||||
profile_context_owner.unregister_rule(id)
|
||||
end
|
||||
|
||||
def only_if
|
||||
return unless block_given?
|
||||
@skip_profile ||= !yield
|
||||
end
|
||||
|
||||
alias_method :rule, :control
|
||||
alias_method :skip_rule, :skip_control
|
||||
|
||||
private
|
||||
|
||||
def block_location(block, alternate_caller)
|
||||
if block.nil?
|
||||
alternate_caller[/^(.+:\d+):in .+$/, 1] || 'unknown'
|
||||
else
|
||||
path, line = block.source_location
|
||||
"#{File.basename(path)}:#{line}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,14 +18,22 @@ module Inspec
|
|||
# @param cwd [String] Current working directory for relative path includes
|
||||
# @param vendor_path [String] Path to the vendor directory
|
||||
#
|
||||
def self.from_lockfile(lockfile, cwd, vendor_path)
|
||||
def self.from_lockfile(lockfile, cwd, vendor_path, backend)
|
||||
vendor_index = VendorIndex.new(vendor_path)
|
||||
dep_tree = lockfile.deps.map do |dep|
|
||||
Inspec::Requirement.from_lock_entry(dep, cwd, vendor_index)
|
||||
Inspec::Requirement.from_lock_entry(dep, cwd, vendor_index, backend)
|
||||
end
|
||||
|
||||
dep_list = flatten_dep_tree(dep_tree)
|
||||
new(cwd, vendor_path, dep_list)
|
||||
new(cwd, vendor_path, dep_list, backend)
|
||||
end
|
||||
|
||||
def self.from_array(dependencies, cwd, vendor_path, backend)
|
||||
dep_list = {}
|
||||
dependencies.each do |d|
|
||||
dep_list[d.name] = d
|
||||
end
|
||||
new(cwd, vendor_path, dep_list, backend)
|
||||
end
|
||||
|
||||
# This is experimental code to test the working of the
|
||||
|
@ -50,14 +58,21 @@ module Inspec
|
|||
# @param cwd [String] current working directory for relative path includes
|
||||
# @param vendor_path [String] path which contains vendored dependencies
|
||||
# @return [dependencies] this
|
||||
def initialize(cwd, vendor_path, dep_list = nil)
|
||||
def initialize(cwd, vendor_path, dep_list, backend)
|
||||
@cwd = cwd
|
||||
@vendor_path = vendor_path
|
||||
@dep_list = dep_list
|
||||
@backend = backend
|
||||
end
|
||||
|
||||
def each
|
||||
@dep_list.each do |_k, v|
|
||||
yield v.profile
|
||||
end
|
||||
end
|
||||
|
||||
def list
|
||||
@dep_list
|
||||
@dep_list || {}
|
||||
end
|
||||
|
||||
def to_array
|
||||
|
@ -77,7 +92,7 @@ module Inspec
|
|||
def vendor(dependencies)
|
||||
return nil if dependencies.nil? || dependencies.empty?
|
||||
@vendor_index ||= VendorIndex.new(@vendor_path)
|
||||
@dep_list = Resolver.resolve(dependencies, @vendor_index, @cwd)
|
||||
@dep_list = Resolver.resolve(dependencies, @vendor_index, @cwd, @backend)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# encoding: utf-8
|
||||
require 'inspec/fetcher'
|
||||
require 'inspec/dependencies/dependency_set'
|
||||
require 'digest'
|
||||
|
||||
module Inspec
|
||||
|
@ -18,17 +19,18 @@ module Inspec
|
|||
new(name, version, vendor_index, opts[:cwd], opts.merge(dep))
|
||||
end
|
||||
|
||||
def self.from_lock_entry(entry, cwd, vendor_index)
|
||||
def self.from_lock_entry(entry, cwd, vendor_index, backend)
|
||||
req = new(entry['name'],
|
||||
entry['version_constraints'],
|
||||
vendor_index,
|
||||
cwd, { url: entry['resolved_source'] })
|
||||
cwd,
|
||||
{ url: entry['resolved_source'],
|
||||
backend: backend })
|
||||
|
||||
locked_deps = []
|
||||
Array(entry['dependencies']).each do |dep_entry|
|
||||
locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, vendor_index)
|
||||
locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, vendor_index, backend)
|
||||
end
|
||||
|
||||
req.lock_deps(locked_deps)
|
||||
req
|
||||
end
|
||||
|
@ -38,6 +40,7 @@ module Inspec
|
|||
@version_requirement = Gem::Requirement.new(Array(version_constraints))
|
||||
@dep = Gem::Dependency.new(name, @version_requirement, :runtime)
|
||||
@vendor_index = vendor_index
|
||||
@backend = opts[:backend]
|
||||
@opts = opts
|
||||
@cwd = cwd
|
||||
end
|
||||
|
@ -123,7 +126,7 @@ module Inspec
|
|||
|
||||
def dependencies
|
||||
@dependencies ||= profile.metadata.dependencies.map do |r|
|
||||
Inspec::Requirement.from_metadata(r, @vendor_index, cwd: @cwd)
|
||||
Inspec::Requirement.from_metadata(r, @vendor_index, cwd: @cwd, backend: @backend)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -137,7 +140,11 @@ module Inspec
|
|||
|
||||
def profile
|
||||
return nil if path.nil?
|
||||
@profile ||= Inspec::Profile.for_target(path, {})
|
||||
opts = { backend: @backend }
|
||||
if !@dependencies.nil?
|
||||
opts[:dependencies] = Inspec::DependencySet.from_array(@dependencies, @cwd, @vendor_index, @backend)
|
||||
end
|
||||
@profile ||= Inspec::Profile.for_target(path, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,9 +23,9 @@ module Inspec
|
|||
# implementation of the fetcher being used.
|
||||
#
|
||||
class Resolver
|
||||
def self.resolve(dependencies, vendor_index, working_dir)
|
||||
def self.resolve(dependencies, vendor_index, working_dir, backend)
|
||||
reqs = dependencies.map do |dep|
|
||||
req = Inspec::Requirement.from_metadata(dep, vendor_index, cwd: working_dir)
|
||||
req = Inspec::Requirement.from_metadata(dep, vendor_index, cwd: working_dir, backend: backend)
|
||||
req || fail("Cannot initialize dependency: #{req}")
|
||||
end
|
||||
new.resolve(reqs)
|
||||
|
|
|
@ -18,19 +18,6 @@ module Inspec::DSL
|
|||
alias require_rules require_controls
|
||||
alias include_rules include_controls
|
||||
|
||||
def self.rule_from_check(m, a, b)
|
||||
if a.is_a?(Array) && !a.empty? &&
|
||||
a[0].respond_to?(:resource_skipped) &&
|
||||
!a[0].resource_skipped.nil?
|
||||
::Inspec::Rule.__send__(m, *a) do
|
||||
it a[0].resource_skipped
|
||||
end
|
||||
else
|
||||
# execute the method
|
||||
::Inspec::Rule.__send__(m, *a, &b)
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_spec_files_for_profile(bind_context, opts, &block)
|
||||
dependencies = opts[:dependencies]
|
||||
profile_id = opts[:profile_id]
|
||||
|
@ -43,25 +30,21 @@ of #{bind_context.profile_name}.
|
|||
|
||||
Dependencies available from this context are:
|
||||
|
||||
#{dependencies.list.keys.join("\n ")}
|
||||
#{dependencies.list.keys.join("\n ")}
|
||||
EOF
|
||||
end
|
||||
|
||||
context = load_profile_context(dep_entry.profile, opts[:backend])
|
||||
|
||||
context = dep_entry.profile.runner_context
|
||||
# if we don't want all the rules, then just make 1 pass to get all rule_IDs
|
||||
# that we want to keep from the original
|
||||
filter_included_controls(context, dep_entry.profile, &block) if !opts[:include_all]
|
||||
|
||||
# interpret the block and skip/modify as required
|
||||
context.load(block) if block_given?
|
||||
|
||||
bind_context.add_subcontext(context)
|
||||
end
|
||||
|
||||
def self.filter_included_controls(context, profile, &block)
|
||||
mock = Inspec::Backend.create({ backend: 'mock' })
|
||||
include_ctx = Inspec::ProfileContext.for_profile(profile, mock)
|
||||
include_ctx = profile.runner_context
|
||||
include_ctx.load(block) if block_given?
|
||||
# remove all rules that were not registered
|
||||
context.rules.keys.each do |id|
|
||||
|
@ -70,16 +53,4 @@ EOF
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_profile_context(profile, backend)
|
||||
ctx = Inspec::ProfileContext.for_profile(profile, backend)
|
||||
profile.libraries.each do |path, content|
|
||||
ctx.load(content.to_s, path, 1)
|
||||
ctx.reload_dsl
|
||||
end
|
||||
profile.tests.each do |path, content|
|
||||
ctx.load(content.to_s, path, 1)
|
||||
end
|
||||
ctx
|
||||
end
|
||||
end
|
||||
|
|
25
lib/inspec/dsl_shared.rb
Normal file
25
lib/inspec/dsl_shared.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# encoding: utf-8
|
||||
module Inspec
|
||||
#
|
||||
# Contains methods we would like in multiple DSL
|
||||
#
|
||||
module DSL
|
||||
module RequireOverride
|
||||
# Save the toplevel require method to load all ruby dependencies.
|
||||
# It is used whenever the `require 'lib'` is not in libraries.
|
||||
alias __ruby_require require
|
||||
|
||||
def require(path)
|
||||
rbpath = path + '.rb'
|
||||
return __ruby_require(path) if !@require_loader.exists?(rbpath)
|
||||
return false if @require_loader.loaded?(rbpath)
|
||||
|
||||
# This is equivalent to calling `require 'lib'` with lib on disk.
|
||||
# We cannot rely on libraries residing on disk however.
|
||||
# TODO: Sandboxing.
|
||||
content, path, line = @require_loader.load(rbpath)
|
||||
eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Lint/Eval
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
47
lib/inspec/library_eval_context.rb
Normal file
47
lib/inspec/library_eval_context.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# encoding: utf-8
|
||||
# author: Steven Danna
|
||||
# author: Victoria Jeffrey
|
||||
require 'inspec/plugins/resource'
|
||||
require 'inspec/dsl_shared'
|
||||
|
||||
module Inspec
|
||||
#
|
||||
# LibaryEvalContext constructs an instance of an anonymous class
|
||||
# that library files will be instance_exec'd against.
|
||||
#
|
||||
# The anonymous class ensures that `Inspec.resource(1)` will return
|
||||
# an anonymouse class that is suitable as the parent class of an
|
||||
# inspec resource. The class returned will have the resource
|
||||
# registry used by all dsl methods bound to the resource registry
|
||||
# passed into the #create constructor.
|
||||
#
|
||||
#
|
||||
class LibraryEvalContext
|
||||
def self.create(registry, require_loader)
|
||||
c = Class.new do
|
||||
extend Inspec::ResourceDSL
|
||||
include Inspec::ResourceBehaviors
|
||||
define_singleton_method :__resource_registry do
|
||||
registry
|
||||
end
|
||||
end
|
||||
|
||||
c2 = Class.new do
|
||||
define_singleton_method :resource do |version|
|
||||
Inspec.validate_resource_dsl_version!(version)
|
||||
c
|
||||
end
|
||||
end
|
||||
|
||||
c3 = Class.new do
|
||||
include Inspec::DSL::RequireOverride
|
||||
def initialize(require_loader) # rubocop:disable Lint/NestedMethodDefinition
|
||||
@require_loader = require_loader
|
||||
end
|
||||
end
|
||||
|
||||
c3.const_set(:Inspec, c2)
|
||||
c3.new(require_loader)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,83 +3,83 @@
|
|||
# author: Christoph Hartmann
|
||||
|
||||
module Inspec
|
||||
module Plugins
|
||||
class Resource
|
||||
def self.name(name = nil)
|
||||
return if name.nil?
|
||||
@name = name
|
||||
Inspec::Plugins::Resource.__register(name, self)
|
||||
end
|
||||
|
||||
def self.desc(description = nil)
|
||||
return if description.nil?
|
||||
Inspec::Resource.registry[@name].desc(description)
|
||||
end
|
||||
|
||||
def self.example(example = nil)
|
||||
return if example.nil?
|
||||
Inspec::Resource.registry[@name].example(example)
|
||||
end
|
||||
|
||||
def self.__register(name, obj)
|
||||
# rubocop:disable Lint/NestedMethodDefinition, Lint/DuplicateMethods
|
||||
cl = Class.new(obj) do
|
||||
# add some common methods
|
||||
include Inspec::Plugins::ResourceCommon
|
||||
def initialize(backend, name, *args)
|
||||
# attach the backend to this instance
|
||||
@__backend_runner__ = backend
|
||||
@__resource_name__ = name
|
||||
# call the resource initializer
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def self.desc(description = nil)
|
||||
return @description if description.nil?
|
||||
@description = description
|
||||
end
|
||||
|
||||
def self.example(example = nil)
|
||||
return @example if example.nil?
|
||||
@example = example
|
||||
end
|
||||
|
||||
def inspec
|
||||
@__backend_runner__
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/NestedMethodDefinition
|
||||
|
||||
# add the resource to the registry by name with a newly-named registry class
|
||||
klass_name = name.split('_').map(&:capitalize).join
|
||||
Inspec::Resource::Registry.const_set(klass_name, cl)
|
||||
Inspec::Resource.registry[name] = Inspec::Resource::Registry.const_get(klass_name)
|
||||
end
|
||||
|
||||
# Define methods which are available to all resources
|
||||
# and may be overwritten.
|
||||
|
||||
# Print the name of the resource
|
||||
def to_s
|
||||
@__resource_name__
|
||||
end
|
||||
|
||||
# Overwrite inspect to provide better output to RSpec results.
|
||||
#
|
||||
# @return [String] full name of the resource
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
module ResourceBehaviors
|
||||
def to_s
|
||||
@__resource_name__
|
||||
end
|
||||
|
||||
module ResourceCommon
|
||||
def resource_skipped
|
||||
@resource_skipped
|
||||
# Overwrite inspect to provide better output to RSpec results.
|
||||
#
|
||||
# @return [String] full name of the resource
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
end
|
||||
|
||||
module ResourceDSL
|
||||
def name(name = nil)
|
||||
return if name.nil?
|
||||
@name = name
|
||||
__register(name, self)
|
||||
end
|
||||
|
||||
def desc(description = nil)
|
||||
return if description.nil?
|
||||
__resource_registry[@name].desc(description)
|
||||
end
|
||||
|
||||
def example(example = nil)
|
||||
return if example.nil?
|
||||
__resource_registry[@name].example(example)
|
||||
end
|
||||
|
||||
def __resource_registry
|
||||
Inspec::Resource.registry
|
||||
end
|
||||
|
||||
def __register(name, obj)
|
||||
# rubocop:disable Lint/NestedMethodDefinition
|
||||
cl = Class.new(obj) do
|
||||
def initialize(backend, name, *args)
|
||||
# attach the backend to this instance
|
||||
@__backend_runner__ = backend
|
||||
@__resource_name__ = name
|
||||
# call the resource initializer
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def self.desc(description = nil)
|
||||
return @description if description.nil?
|
||||
@description = description
|
||||
end
|
||||
|
||||
def self.example(example = nil)
|
||||
return @example if example.nil?
|
||||
@example = example
|
||||
end
|
||||
|
||||
def resource_skipped
|
||||
@resource_skipped
|
||||
end
|
||||
|
||||
def skip_resource(message)
|
||||
@resource_skipped = message
|
||||
end
|
||||
|
||||
def inspec
|
||||
@__backend_runner__
|
||||
end
|
||||
end
|
||||
|
||||
def skip_resource(message)
|
||||
@resource_skipped = message
|
||||
end
|
||||
# rubocop:enable Lint/NestedMethodDefinition
|
||||
__resource_registry[name] = cl
|
||||
end
|
||||
end
|
||||
|
||||
module Plugins
|
||||
class Resource
|
||||
extend Inspec::ResourceDSL
|
||||
include Inspec::ResourceBehaviors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,9 @@ require 'inspec/polyfill'
|
|||
require 'inspec/fetcher'
|
||||
require 'inspec/source_reader'
|
||||
require 'inspec/metadata'
|
||||
require 'inspec/backend'
|
||||
require 'inspec/rule'
|
||||
require 'inspec/profile_context'
|
||||
require 'inspec/dependencies/lockfile'
|
||||
require 'inspec/dependencies/dependency_set'
|
||||
|
||||
|
@ -31,7 +34,7 @@ module Inspec
|
|||
reader
|
||||
end
|
||||
|
||||
def self.for_target(target, opts)
|
||||
def self.for_target(target, opts = {})
|
||||
new(resolve_target(target), opts.merge(target: target))
|
||||
end
|
||||
|
||||
|
@ -47,9 +50,14 @@ module Inspec
|
|||
@target = @options.delete(:target)
|
||||
@logger = @options[:logger] || Logger.new(nil)
|
||||
@source_reader = source_reader
|
||||
if options[:dependencies]
|
||||
@locked_dependencies = options[:dependencies]
|
||||
end
|
||||
@controls = options[:controls] || []
|
||||
@profile_id = @options[:id]
|
||||
@runner_context = nil
|
||||
@backend = @options[:backend] || Inspec::Backend.create(options)
|
||||
Metadata.finalize(@source_reader.metadata, @profile_id)
|
||||
@runner_context = @options[:profile_context] || Inspec::ProfileContext.for_profile(self, @backend)
|
||||
end
|
||||
|
||||
def name
|
||||
|
@ -60,6 +68,46 @@ module Inspec
|
|||
@params ||= load_params
|
||||
end
|
||||
|
||||
def collect_tests(include_list = @controls)
|
||||
if !@tests_collected
|
||||
locked_dependencies.each(&:collect_tests)
|
||||
|
||||
tests.each do |path, content|
|
||||
next if content.nil? || content.empty?
|
||||
abs_path = source_reader.target.abs_path(path)
|
||||
@runner_context.load_control_file(content, abs_path, nil)
|
||||
end
|
||||
@tests_collected = true
|
||||
end
|
||||
filter_controls(@runner_context.all_rules, include_list)
|
||||
end
|
||||
|
||||
def filter_controls(controls_array, include_list)
|
||||
return controls_array if include_list.nil? || include_list.empty?
|
||||
controls_array.select do |c|
|
||||
id = ::Inspec::Rule.rule_id(c)
|
||||
include_list.include?(id)
|
||||
end
|
||||
end
|
||||
|
||||
def load_libraries
|
||||
locked_dependencies.each do |d|
|
||||
c = d.load_libraries
|
||||
@runner_context.add_resources(c)
|
||||
end
|
||||
|
||||
libs = libraries.map do |path, content|
|
||||
[content, path]
|
||||
end
|
||||
|
||||
@runner_context.load_libraries(libs)
|
||||
@runner_context
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Inspec::Profile<#{name}>"
|
||||
end
|
||||
|
||||
def info
|
||||
res = params.dup
|
||||
# add information about the controls
|
||||
|
@ -248,11 +296,15 @@ module Inspec
|
|||
# @return [Inspec::Lockfile]
|
||||
#
|
||||
def generate_lockfile(vendor_path = nil)
|
||||
res = Inspec::DependencySet.new(cwd, vendor_path)
|
||||
res = Inspec::DependencySet.new(cwd, vendor_path, nil, @backend)
|
||||
res.vendor(metadata.dependencies)
|
||||
Inspec::Lockfile.from_dependency_set(res)
|
||||
end
|
||||
|
||||
def load_dependencies
|
||||
Inspec::DependencySet.from_lockfile(lockfile, cwd, nil, @backend)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Create an archive name for this profile and an additional options
|
||||
|
@ -281,34 +333,17 @@ module Inspec
|
|||
params
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a new runner for the current profile.
|
||||
#
|
||||
# @params [Symbol] The type of backend to use when constructing a
|
||||
# new runner.
|
||||
# @returns [Inspec::Runner]
|
||||
#
|
||||
def runner_for_profile(backend = :mock)
|
||||
opts = @options.dup
|
||||
opts[:ignore_supports] = true
|
||||
r = Runner.new(id: @profile_id,
|
||||
backend: backend,
|
||||
test_collector: opts.delete(:test_collector))
|
||||
r.add_profile(self, opts)
|
||||
r
|
||||
end
|
||||
|
||||
def load_checks_params(params)
|
||||
load_libraries
|
||||
tests = collect_tests
|
||||
params[:controls] = controls = {}
|
||||
params[:groups] = groups = {}
|
||||
prefix = @source_reader.target.prefix || ''
|
||||
# Load from the runner_context if it exists
|
||||
runner = @runner_context || runner_for_profile
|
||||
runner.all_rules.each do |rule|
|
||||
tests.each do |rule|
|
||||
f = load_rule_filepath(prefix, rule)
|
||||
load_rule(rule, f, controls, groups)
|
||||
end
|
||||
params[:attributes] = runner.attributes
|
||||
params[:attributes] = @runner_context.attributes
|
||||
params
|
||||
end
|
||||
|
||||
|
@ -337,9 +372,5 @@ module Inspec
|
|||
}
|
||||
groups[file][:controls].push(id)
|
||||
end
|
||||
|
||||
def load_dependencies
|
||||
Inspec::DependencySet.from_lockfile(lockfile, cwd, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
# author: Christoph Hartmann
|
||||
require 'inspec/log'
|
||||
require 'inspec/rule'
|
||||
require 'inspec/dsl'
|
||||
require 'inspec/resource'
|
||||
require 'inspec/library_eval_context'
|
||||
require 'inspec/control_eval_context'
|
||||
require 'inspec/require_loader'
|
||||
require 'securerandom'
|
||||
require 'inspec/objects/attribute'
|
||||
|
@ -14,7 +16,7 @@ module Inspec
|
|||
new(profile.name, backend, { 'profile' => profile })
|
||||
end
|
||||
|
||||
attr_reader :attributes, :rules, :profile_id
|
||||
attr_reader :attributes, :rules, :profile_id, :resource_registry
|
||||
def initialize(profile_id, backend, conf)
|
||||
if backend.nil?
|
||||
fail 'ProfileContext is initiated with a backend == nil. ' \
|
||||
|
@ -25,17 +27,35 @@ module Inspec
|
|||
@conf = conf.dup
|
||||
@rules = {}
|
||||
@subcontexts = []
|
||||
@dependencies = {}
|
||||
@dependencies = conf['profile'].locked_dependencies unless conf['profile'].nil?
|
||||
@require_loader = ::Inspec::RequireLoader.new
|
||||
@attributes = []
|
||||
reload_dsl
|
||||
# A local resource registry that only contains resources defined
|
||||
# in the transitive dependency tree of the loaded profile.
|
||||
@resource_registry = Inspec::Resource.new_registry
|
||||
@library_eval_context = Inspec::LibraryEvalContext.create(@resource_registry, @require_loader)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
if @conf['profile'].nil?
|
||||
{}
|
||||
else
|
||||
@conf['profile'].locked_dependencies
|
||||
end
|
||||
end
|
||||
|
||||
def to_resources_dsl
|
||||
Inspec::Resource.create_dsl(@backend, @resource_registry)
|
||||
end
|
||||
|
||||
def control_eval_context
|
||||
@control_eval_context ||= begin
|
||||
ctx = Inspec::ControlEvalContext.create(self, to_resources_dsl)
|
||||
ctx.new(@backend, @conf, dependencies, @require_loader)
|
||||
end
|
||||
end
|
||||
|
||||
def reload_dsl
|
||||
resources_dsl = Inspec::Resource.create_dsl(@backend)
|
||||
ctx = create_context(resources_dsl, rule_context(resources_dsl))
|
||||
@profile_context = ctx.new(@backend, @conf, @dependencies, @require_loader)
|
||||
@control_eval_context = nil
|
||||
end
|
||||
|
||||
def all_rules
|
||||
|
@ -44,16 +64,14 @@ module Inspec
|
|||
ret
|
||||
end
|
||||
|
||||
def add_subcontext(context)
|
||||
@subcontexts << context
|
||||
def add_resources(context)
|
||||
@resource_registry.merge!(context.resource_registry)
|
||||
control_eval_context.add_resources(context)
|
||||
reload_dsl
|
||||
end
|
||||
|
||||
def load_tests(tests)
|
||||
tests.each do |t|
|
||||
content = t[:content]
|
||||
next if content.nil? || content.empty?
|
||||
load(content, t[:ref], t[:line])
|
||||
end
|
||||
def add_subcontext(context)
|
||||
@subcontexts << context
|
||||
end
|
||||
|
||||
def load_libraries(libs)
|
||||
|
@ -73,21 +91,29 @@ module Inspec
|
|||
# load all files directly that are flat inside the libraries folder
|
||||
autoloads.each do |path|
|
||||
next unless path.end_with?('.rb')
|
||||
load(*@require_loader.load(path)) unless @require_loader.loaded?(path)
|
||||
load_library_file(*@require_loader.load(path)) unless @require_loader.loaded?(path)
|
||||
end
|
||||
|
||||
reload_dsl
|
||||
end
|
||||
|
||||
def load(content, source = nil, line = nil)
|
||||
def load_control_file(*args)
|
||||
load_with_context(control_eval_context, *args)
|
||||
end
|
||||
alias load load_control_file
|
||||
|
||||
def load_library_file(*args)
|
||||
load_with_context(@library_eval_context, *args)
|
||||
end
|
||||
|
||||
def load_with_context(context, content, source = nil, line = nil)
|
||||
Inspec::Log.debug("Loading #{source || '<anonymous content>'} into #{self}")
|
||||
@current_load = { file: source }
|
||||
if content.is_a? Proc
|
||||
@profile_context.instance_eval(&content)
|
||||
context.instance_eval(&content)
|
||||
elsif source.nil? && line.nil?
|
||||
@profile_context.instance_eval(content)
|
||||
context.instance_eval(content)
|
||||
else
|
||||
@profile_context.instance_eval(content, source || 'unknown', line || 1)
|
||||
context.instance_eval(content, source || 'unknown', line || 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -129,131 +155,5 @@ module Inspec
|
|||
return rid.to_s if pid.to_s.empty?
|
||||
pid.to_s + '/' + rid.to_s
|
||||
end
|
||||
|
||||
# Create the context for controls. This includes all components of the DSL,
|
||||
# including matchers and resources.
|
||||
#
|
||||
# @param [ResourcesDSL] resources_dsl which has all resources to attach
|
||||
# @return [RuleContext] the inner context of rules
|
||||
def rule_context(resources_dsl)
|
||||
require 'rspec/core/dsl'
|
||||
Class.new(Inspec::Rule) do
|
||||
include RSpec::Core::DSL
|
||||
include resources_dsl
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the heart of the profile context:
|
||||
# An instantiated object which has all resources registered to it
|
||||
# and exposes them to the a test file. The profile context serves as a
|
||||
# container for all profiles which are registered. Within the context
|
||||
# profiles get access to all DSL calls for creating tests and controls.
|
||||
#
|
||||
# @param outer_dsl [OuterDSLClass]
|
||||
# @return [ProfileContextClass]
|
||||
def create_context(resources_dsl, rule_class) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
||||
profile_context_owner = self
|
||||
profile_id = @profile_id
|
||||
|
||||
# rubocop:disable Lint/NestedMethodDefinition
|
||||
Class.new do
|
||||
include Inspec::DSL
|
||||
include resources_dsl
|
||||
|
||||
def initialize(backend, conf, dependencies, require_loader) # rubocop:disable Lint/NestedMethodDefinition, Lint/DuplicateMethods
|
||||
@backend = backend
|
||||
@conf = conf
|
||||
@dependencies = dependencies
|
||||
@require_loader = require_loader
|
||||
@skip_profile = false
|
||||
end
|
||||
|
||||
# Save the toplevel require method to load all ruby dependencies.
|
||||
# It is used whenever the `require 'lib'` is not in libraries.
|
||||
alias_method :__ruby_require, :require
|
||||
|
||||
def require(path)
|
||||
rbpath = path + '.rb'
|
||||
return __ruby_require(path) if !@require_loader.exists?(rbpath)
|
||||
return false if @require_loader.loaded?(rbpath)
|
||||
|
||||
# This is equivalent to calling `require 'lib'` with lib on disk.
|
||||
# We cannot rely on libraries residing on disk however.
|
||||
# TODO: Sandboxing.
|
||||
content, path, line = @require_loader.load(rbpath)
|
||||
eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Lint/Eval
|
||||
end
|
||||
|
||||
define_method :title do |arg|
|
||||
profile_context_owner.set_header(:title, arg)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Profile Context Run #{profile_name}"
|
||||
end
|
||||
|
||||
define_method :profile_name do
|
||||
profile_id
|
||||
end
|
||||
|
||||
define_method :control do |*args, &block|
|
||||
id = args[0]
|
||||
opts = args[1] || {}
|
||||
register_control(rule_class.new(id, profile_id, opts, &block))
|
||||
end
|
||||
|
||||
define_method :describe do |*args, &block|
|
||||
loc = block_location(block, caller[0])
|
||||
id = "(generated from #{loc} #{SecureRandom.hex})"
|
||||
|
||||
res = nil
|
||||
rule = rule_class.new(id, profile_id, {}) do
|
||||
res = describe(*args, &block)
|
||||
end
|
||||
register_control(rule, &block)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
define_method :add_subcontext do |context|
|
||||
profile_context_owner.add_subcontext(context)
|
||||
end
|
||||
|
||||
define_method :register_control do |control, &block|
|
||||
::Inspec::Rule.set_skip_rule(control, true) if @skip_profile
|
||||
|
||||
profile_context_owner.register_rule(control, &block) unless control.nil?
|
||||
end
|
||||
|
||||
# method for attributes; import attribute handling
|
||||
define_method :attribute do |name, options|
|
||||
profile_context_owner.register_attribute(name, options)
|
||||
end
|
||||
|
||||
define_method :skip_control do |id|
|
||||
profile_context_owner.unregister_rule(id)
|
||||
end
|
||||
|
||||
def only_if
|
||||
return unless block_given?
|
||||
@skip_profile ||= !yield
|
||||
end
|
||||
|
||||
alias_method :rule, :control
|
||||
alias_method :skip_rule, :skip_control
|
||||
|
||||
private
|
||||
|
||||
def block_location(block, alternate_caller)
|
||||
if block.nil?
|
||||
alternate_caller[/^(.+:\d+):in .+$/, 1] || 'unknown'
|
||||
else
|
||||
path, line = block.source_location
|
||||
"#{File.basename(path)}:#{line}"
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,17 +3,20 @@
|
|||
# license: All rights reserved
|
||||
# author: Dominik Richter
|
||||
# author: Christoph Hartmann
|
||||
|
||||
require 'inspec/plugins'
|
||||
|
||||
module Inspec
|
||||
class Resource
|
||||
class Registry
|
||||
# empty class for namespacing resource classes in the registry
|
||||
def self.default_registry
|
||||
@default_registry ||= {}
|
||||
end
|
||||
|
||||
def self.registry
|
||||
@registry ||= {}
|
||||
@registry ||= default_registry
|
||||
end
|
||||
|
||||
def self.new_registry
|
||||
default_registry.dup
|
||||
end
|
||||
|
||||
# Creates the inner DSL which includes all resources for
|
||||
|
@ -22,9 +25,8 @@ module Inspec
|
|||
#
|
||||
# @param backend [BackendRunner] exposing the target to resources
|
||||
# @return [ResourcesDSL]
|
||||
def self.create_dsl(backend)
|
||||
def self.create_dsl(backend, my_registry = registry)
|
||||
# need the local name, to use it in the module creation further down
|
||||
my_registry = registry
|
||||
Module.new do
|
||||
my_registry.each do |id, r|
|
||||
define_method id.to_sym do |*args|
|
||||
|
@ -41,10 +43,14 @@ module Inspec
|
|||
# @param [int] version the resource version to use
|
||||
# @return [Resource] base class for creating a new resource
|
||||
def self.resource(version)
|
||||
validate_resource_dsl_version!(version)
|
||||
Inspec::Plugins::Resource
|
||||
end
|
||||
|
||||
def self.validate_resource_dsl_version!(version)
|
||||
if version != 1
|
||||
fail 'Only resource version 1 is supported!'
|
||||
end
|
||||
Inspec::Plugins::Resource
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ module Inspec
|
|||
instance_eval(&block) if block_given?
|
||||
end
|
||||
|
||||
def to_s
|
||||
Inspec::Rule.rule_id(self)
|
||||
end
|
||||
|
||||
def id(*_)
|
||||
# never overwrite the ID
|
||||
@id
|
||||
|
|
|
@ -30,11 +30,18 @@ module Inspec
|
|||
#
|
||||
class Runner # rubocop:disable Metrics/ClassLength
|
||||
extend Forwardable
|
||||
|
||||
def_delegator :@test_collector, :report
|
||||
def_delegator :@test_collector, :reset
|
||||
|
||||
attr_reader :backend, :rules, :attributes
|
||||
def initialize(conf = {})
|
||||
@rules = []
|
||||
@conf = conf.dup
|
||||
@conf[:logger] ||= Logger.new(nil)
|
||||
@target_profiles = []
|
||||
@controls = @conf[:controls] || []
|
||||
@ignore_supports = @conf[:ignore_supports]
|
||||
|
||||
@test_collector = @conf.delete(:test_collector) || begin
|
||||
require 'inspec/runner_rspec'
|
||||
|
@ -52,19 +59,31 @@ module Inspec
|
|||
@test_collector.tests
|
||||
end
|
||||
|
||||
def normalize_map(hm)
|
||||
res = {}
|
||||
hm.each {|k, v|
|
||||
res[k.to_s] = v
|
||||
}
|
||||
res
|
||||
end
|
||||
|
||||
def configure_transport
|
||||
@backend = Inspec::Backend.create(@conf)
|
||||
@test_collector.backend = @backend
|
||||
end
|
||||
|
||||
def run(with = nil)
|
||||
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
||||
Inspec::Log.debug "Backend is #{@backend}"
|
||||
all_controls = []
|
||||
|
||||
@target_profiles.each do |profile|
|
||||
@test_collector.add_profile(profile)
|
||||
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)
|
||||
end
|
||||
|
||||
@test_collector.run(with)
|
||||
end
|
||||
|
||||
# determine all attributes before the execution, fetch data from secrets backend
|
||||
def load_attributes(options)
|
||||
attributes = {}
|
||||
|
@ -82,8 +101,7 @@ module Inspec
|
|||
|
||||
#
|
||||
# add_target allows the user to add a target whose tests will be
|
||||
# run when the user calls the run method. This is the main entry
|
||||
# point to profile loading.
|
||||
# run when the user calls the run method.
|
||||
#
|
||||
# A target is a path or URL that points to a profile. Using this
|
||||
# target we generate a Profile and a ProfileContext. The content
|
||||
|
@ -102,61 +120,15 @@ module Inspec
|
|||
# TODO: Deduplicate/clarify the loading code that exists in here,
|
||||
# the ProfileContext, the Profile, and Inspec::DSL
|
||||
#
|
||||
# TODO: Libraries of dependent profiles should be loaded even when
|
||||
# include_content/required_content aren't called.
|
||||
#
|
||||
# @params target [String] A path or URL to a profile or raw test.
|
||||
#
|
||||
# @params options [Hash] An options hash. The relevant options
|
||||
# considered by this call path are:
|
||||
#
|
||||
# - `:ignore_supports`: A boolean that controls whether to ignore
|
||||
# the profile's metadata regarding supported Inspec versions or
|
||||
# platforms.
|
||||
#
|
||||
# - `:controls`: An array of controls to run from this
|
||||
# profile. Any returned rules that are not part of these
|
||||
# controls will be filtered before being passed to @test_collector.
|
||||
# @params _opts [Hash] Unused, but still here to avoid breaking kitchen-inspec
|
||||
#
|
||||
# @eturns [Inspec::ProfileContext]
|
||||
#
|
||||
def add_target(target, options = {})
|
||||
profile = Inspec::Profile.for_target(target, options)
|
||||
def add_target(target, _opts = [])
|
||||
profile = Inspec::Profile.for_target(target, backend: @backend, controls: @controls)
|
||||
fail "Could not resolve #{target} to valid input." if profile.nil?
|
||||
add_profile(profile, options)
|
||||
end
|
||||
|
||||
# Returns the profile context used to initialize this profile.
|
||||
def add_profile(profile, options = {})
|
||||
return if !options[:ignore_supports] && !supports_profile?(profile)
|
||||
@test_collector.add_profile(profile)
|
||||
add_content_from_profile(profile, options[:controls])
|
||||
end
|
||||
|
||||
def add_content_from_profile(profile, controls)
|
||||
return if profile.tests.nil? || profile.tests.empty?
|
||||
|
||||
ctx = Inspec::ProfileContext.for_profile(profile, @backend)
|
||||
profile.runner_context = ctx
|
||||
|
||||
libs = profile.libraries.map do |k, v|
|
||||
[v, k]
|
||||
end
|
||||
|
||||
tests = profile.tests.map do |ref, content|
|
||||
r = profile.source_reader.target.abs_path(ref)
|
||||
{ ref: r, content: content }
|
||||
end
|
||||
|
||||
ctx.load_libraries(libs)
|
||||
ctx.load_tests(tests)
|
||||
@attributes |= ctx.attributes
|
||||
|
||||
filter_controls(ctx.all_rules, controls).each do |rule|
|
||||
register_rule(rule)
|
||||
end
|
||||
|
||||
ctx
|
||||
@target_profiles << profile if supports_profile?(profile)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -174,7 +146,7 @@ module Inspec
|
|||
end
|
||||
|
||||
def supports_profile?(profile)
|
||||
return true if profile.metadata.nil?
|
||||
return true if profile.metadata.nil? || @ignore_supports
|
||||
|
||||
if !profile.metadata.supports_runtime?
|
||||
fail 'This profile requires InSpec version '\
|
||||
|
@ -207,20 +179,8 @@ module Inspec
|
|||
new_tests
|
||||
end
|
||||
|
||||
def_delegator :@test_collector, :run
|
||||
def_delegator :@test_collector, :report
|
||||
def_delegator :@test_collector, :reset
|
||||
|
||||
private
|
||||
|
||||
def filter_controls(controls_array, include_list)
|
||||
return controls_array if include_list.nil? || include_list.empty?
|
||||
controls_array.select do |c|
|
||||
id = ::Inspec::Rule.rule_id(c)
|
||||
include_list.include?(id)
|
||||
end
|
||||
end
|
||||
|
||||
def block_source_info(block)
|
||||
return {} if block.nil? || !block.respond_to?(:source_location)
|
||||
opts = {}
|
||||
|
@ -265,6 +225,7 @@ module Inspec
|
|||
end
|
||||
|
||||
def register_rule(rule)
|
||||
Inspec::Log.debug "Registering rule #{rule}"
|
||||
@rules << rule
|
||||
checks = ::Inspec::Rule.prepare_checks(rule)
|
||||
examples = checks.map do |m, a, b|
|
||||
|
|
|
@ -50,7 +50,7 @@ class DockerTester
|
|||
puts "--> run test on docker #{container.id}"
|
||||
opts = { 'target' => "docker://#{container.id}" }
|
||||
runner = Inspec::Runner.new(opts)
|
||||
@tests.each { |test| runner.add_target(test, opts) }
|
||||
@tests.each { |test| runner.add_target(test) }
|
||||
runner.tests.map { |g| g.run(report) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -292,6 +292,7 @@ class MockLoader
|
|||
|
||||
def self.load_profile(name, opts = {})
|
||||
opts[:test_collector] = Inspec::RunnerMock.new
|
||||
opts[:backend] = Inspec::Backend.create(opts)
|
||||
Inspec::Profile.for_target(profile_path(name), opts)
|
||||
end
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ describe 'controls' do
|
|||
'inspec.yml' => "name: mock",
|
||||
'controls/mock.rb' => "control '1' do\n#{content}\nend\n",
|
||||
}
|
||||
opts = { test_collector: Inspec::RunnerMock.new }
|
||||
opts = { test_collector: Inspec::RunnerMock.new, backend: Inspec::Backend.create({ backend: 'mock' }) }
|
||||
Inspec::Profile.for_target(data, opts)
|
||||
.params[:controls]['1']
|
||||
end
|
||||
|
|
|
@ -2,8 +2,3 @@
|
|||
|
||||
include_controls 'profile_a'
|
||||
include_controls 'profile_b'
|
||||
include_controls 'ssh-hardening' do
|
||||
12.upto(12) do |i|
|
||||
skip_control "ssh-%02d" % i
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,5 +10,3 @@ depends:
|
|||
path: ../profile_a
|
||||
- name: profile_b
|
||||
path: ../profile_b
|
||||
- name: ssh-hardening
|
||||
url: https://github.com/dev-sec/tests-ssh-hardening/archive/master.tar.gz
|
||||
|
|
|
@ -20,3 +20,9 @@ control 'profilea-1' do # A unique ID for this control
|
|||
it { should be_directory }
|
||||
end
|
||||
end
|
||||
|
||||
control 'profilea-2' do
|
||||
describe gordon_config do
|
||||
its('version') { should eq('1.0') }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,3 +14,9 @@ control 'profileb-1' do # A unique ID for this control
|
|||
it { should be_directory }
|
||||
end
|
||||
end
|
||||
|
||||
control 'profileb-2' do
|
||||
describe gordon_config do
|
||||
its('version') { should eq('2.0') }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
class GordonConfig < Inspec.resource(1)
|
||||
name 'gordon_config'
|
||||
|
||||
desc "Gordon's resource description ..."
|
||||
|
||||
example "
|
||||
describe gordon_config do
|
||||
its('version') { should eq('1.0') }
|
||||
end
|
||||
"
|
||||
|
||||
def version
|
||||
"1.0"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class GordonConfig < Inspec.resource(1)
|
||||
name 'gordon_config'
|
||||
|
||||
desc "Gordon's resource description ..."
|
||||
|
||||
example "
|
||||
describe gordon_config do
|
||||
its('version') { should eq('2.0') }
|
||||
end
|
||||
"
|
||||
|
||||
def version
|
||||
"2.0"
|
||||
end
|
||||
end
|
|
@ -14,12 +14,6 @@ describe Inspec::Plugins::Resource do
|
|||
Class.new(base) do name 'hello'; end
|
||||
Inspec::Resource.registry['hello'].wont_be :nil?
|
||||
end
|
||||
|
||||
it "will create a good class name" do
|
||||
Class.new(base) do name 'hello_world'; end
|
||||
Inspec::Resource.registry['hello_world'].to_s
|
||||
.must_equal 'Inspec::Resource::Registry::HelloWorld'
|
||||
end
|
||||
end
|
||||
|
||||
def create(&block)
|
||||
|
|
40
test/unit/profiles/library_eval_context_tests.rb
Normal file
40
test/unit/profiles/library_eval_context_tests.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
# encoding: utf-8
|
||||
# author: Steven Danna
|
||||
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
require 'inspec/library_eval_context'
|
||||
|
||||
describe Inspec::LibraryEvalContext do
|
||||
let(:resource_content) { <<EOF
|
||||
class MyTestResource < Inspec.resource(1)
|
||||
name 'my_test_resource'
|
||||
|
||||
desc 'A test description'
|
||||
example 'Forgot to write docs, sorry'
|
||||
|
||||
def version
|
||||
'2.0'
|
||||
end
|
||||
end
|
||||
EOF
|
||||
}
|
||||
|
||||
let(:registry) { Inspec::Resource.new_registry }
|
||||
let(:eval_context) { Inspec::LibraryEvalContext.create(registry, nil) }
|
||||
|
||||
it 'accepts a registry' do
|
||||
Inspec::LibraryEvalContext.create(registry, nil)
|
||||
end
|
||||
|
||||
it 'adds the resource to our registry' do
|
||||
eval_context.instance_eval(resource_content)
|
||||
registry.keys.include?("my_test_resource").must_equal true
|
||||
end
|
||||
|
||||
it 'adds nothing to the default registry' do
|
||||
old_default_registry = Inspec::Resource.default_registry.dup
|
||||
eval_context.instance_eval(resource_content)
|
||||
old_default_registry.must_equal Inspec::Resource.default_registry
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue