mirror of
https://github.com/inspec/inspec
synced 2024-11-23 21:23:29 +00:00
Merge pull request #1080 from chef/ssd/resource-namespace
Allow users to reference resources from dependencies
This commit is contained in:
commit
6219598208
10 changed files with 138 additions and 12 deletions
|
@ -92,6 +92,14 @@ module Inspec
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
define_method :add_resource do |name, new_res|
|
||||||
|
resources_dsl.module_exec do
|
||||||
|
define_method name.to_sym do |*args|
|
||||||
|
new_res.new(@backend, name.to_s, *args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
define_method :add_resources do |context|
|
define_method :add_resources do |context|
|
||||||
self.class.class_eval do
|
self.class.class_eval do
|
||||||
include context.to_resources_dsl
|
include context.to_resources_dsl
|
||||||
|
|
|
@ -18,6 +18,15 @@ module Inspec::DSL
|
||||||
alias require_rules require_controls
|
alias require_rules require_controls
|
||||||
alias include_rules include_controls
|
alias include_rules include_controls
|
||||||
|
|
||||||
|
def require_resource(options = {})
|
||||||
|
fail 'You must specify a specific resource name when calling require_resource()' if options[:resource].nil?
|
||||||
|
|
||||||
|
from_profile = options[:profile] || profile_name
|
||||||
|
target_name = options[:as] || options[:resource]
|
||||||
|
res = resource_class(from_profile, options[:resource])
|
||||||
|
add_resource(target_name, res)
|
||||||
|
end
|
||||||
|
|
||||||
def self.load_spec_files_for_profile(bind_context, opts, &block)
|
def self.load_spec_files_for_profile(bind_context, opts, &block)
|
||||||
dependencies = opts[:dependencies]
|
dependencies = opts[:dependencies]
|
||||||
profile_id = opts[:profile_id]
|
profile_id = opts[:profile_id]
|
||||||
|
|
|
@ -72,6 +72,9 @@ module Inspec
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop:enable Lint/NestedMethodDefinition
|
# rubocop:enable Lint/NestedMethodDefinition
|
||||||
|
if __resource_registry.key?(name)
|
||||||
|
Inspec::Log.warn("Overwriting resource #{name}. To reference a specific version of #{name} use the resource() method")
|
||||||
|
end
|
||||||
__resource_registry[name] = cl
|
__resource_registry[name] = cl
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,6 +66,7 @@ module Inspec
|
||||||
@backend = options[:backend] || Inspec::Backend.create(options)
|
@backend = options[:backend] || Inspec::Backend.create(options)
|
||||||
@source_reader = source_reader
|
@source_reader = source_reader
|
||||||
@tests_collected = false
|
@tests_collected = false
|
||||||
|
@libraries_loaded = false
|
||||||
Metadata.finalize(@source_reader.metadata, @profile_id)
|
Metadata.finalize(@source_reader.metadata, @profile_id)
|
||||||
@runner_context = options[:profile_context] || Inspec::ProfileContext.for_profile(self,
|
@runner_context = options[:profile_context] || Inspec::ProfileContext.for_profile(self,
|
||||||
@backend,
|
@backend,
|
||||||
|
@ -125,6 +126,8 @@ module Inspec
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_libraries
|
def load_libraries
|
||||||
|
return @runner_context if @libraries_loaded
|
||||||
|
|
||||||
locked_dependencies.each do |d|
|
locked_dependencies.each do |d|
|
||||||
c = d.load_libraries
|
c = d.load_libraries
|
||||||
@runner_context.add_resources(c)
|
@runner_context.add_resources(c)
|
||||||
|
@ -135,6 +138,7 @@ module Inspec
|
||||||
end
|
end
|
||||||
|
|
||||||
@runner_context.load_libraries(libs)
|
@runner_context.load_libraries(libs)
|
||||||
|
@libraries_loaded = true
|
||||||
@runner_context
|
@runner_context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Inspec
|
||||||
'attributes' => attributes })
|
'attributes' => attributes })
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :attributes, :profile_id, :resource_registry
|
attr_reader :attributes, :profile_id, :resource_registry, :backend
|
||||||
attr_accessor :rules
|
attr_accessor :rules
|
||||||
def initialize(profile_id, backend, conf)
|
def initialize(profile_id, backend, conf)
|
||||||
if backend.nil?
|
if backend.nil?
|
||||||
|
@ -28,7 +28,8 @@ module Inspec
|
||||||
@backend = backend
|
@backend = backend
|
||||||
@conf = conf.dup
|
@conf = conf.dup
|
||||||
@rules = {}
|
@rules = {}
|
||||||
@subcontexts = []
|
@control_subcontexts = []
|
||||||
|
@lib_subcontexts = []
|
||||||
@require_loader = ::Inspec::RequireLoader.new
|
@require_loader = ::Inspec::RequireLoader.new
|
||||||
@attributes = []
|
@attributes = []
|
||||||
# A local resource registry that only contains resources defined
|
# A local resource registry that only contains resources defined
|
||||||
|
@ -46,7 +47,7 @@ module Inspec
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_resources_dsl
|
def to_resources_dsl
|
||||||
Inspec::Resource.create_dsl(@backend, @resource_registry)
|
Inspec::Resource.create_dsl(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def control_eval_context
|
def control_eval_context
|
||||||
|
@ -66,20 +67,34 @@ module Inspec
|
||||||
@conf['profile'].supports_os?
|
@conf['profile'].supports_os?
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_rules
|
def all_controls
|
||||||
ret = @rules.values
|
ret = @rules.values
|
||||||
ret += @subcontexts.map(&:all_rules).flatten
|
ret += @control_subcontexts.map(&:all_rules).flatten
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
alias all_rules all_controls
|
||||||
|
|
||||||
|
def subcontext_by_name(name)
|
||||||
|
found = @lib_subcontexts.find { |c| c.profile_id == name }
|
||||||
|
if !found
|
||||||
|
@lib_subcontexts.each do |c|
|
||||||
|
found = c.subcontext_by_name(name)
|
||||||
|
break if found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
def add_resources(context)
|
def add_resources(context)
|
||||||
@resource_registry.merge!(context.resource_registry)
|
@resource_registry.merge!(context.resource_registry)
|
||||||
control_eval_context.add_resources(context)
|
control_eval_context.add_resources(context)
|
||||||
|
@lib_subcontexts << context
|
||||||
reload_dsl
|
reload_dsl
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_subcontext(context)
|
def add_subcontext(context)
|
||||||
@subcontexts << context
|
@control_subcontexts << context
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_libraries(libs)
|
def load_libraries(libs)
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
require 'inspec/plugins'
|
require 'inspec/plugins'
|
||||||
|
|
||||||
module Inspec
|
module Inspec
|
||||||
|
class ProfileNotFound < StandardError; end
|
||||||
|
|
||||||
class Resource
|
class Resource
|
||||||
def self.default_registry
|
def self.default_registry
|
||||||
@default_registry ||= {}
|
@default_registry ||= {}
|
||||||
|
@ -25,9 +27,22 @@ module Inspec
|
||||||
#
|
#
|
||||||
# @param backend [BackendRunner] exposing the target to resources
|
# @param backend [BackendRunner] exposing the target to resources
|
||||||
# @return [ResourcesDSL]
|
# @return [ResourcesDSL]
|
||||||
def self.create_dsl(backend, my_registry = registry)
|
def self.create_dsl(profile_context)
|
||||||
# need the local name, to use it in the module creation further down
|
backend = profile_context.backend
|
||||||
|
my_registry = profile_context.resource_registry
|
||||||
|
|
||||||
Module.new do
|
Module.new do
|
||||||
|
define_method :resource_class do |profile_name, resource_name|
|
||||||
|
inner_context = if profile_name == profile_context.profile_id
|
||||||
|
profile_context
|
||||||
|
else
|
||||||
|
profile_context.subcontext_by_name(profile_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
fail ProfileNotFound, "Cannot find profile named: #{profile_name}" if inner_context.nil?
|
||||||
|
inner_context.resource_registry[resource_name]
|
||||||
|
end
|
||||||
|
|
||||||
my_registry.each do |id, r|
|
my_registry.each do |id, r|
|
||||||
define_method id.to_sym do |*args|
|
define_method id.to_sym do |*args|
|
||||||
r.new(backend, id.to_s, *args)
|
r.new(backend, id.to_s, *args)
|
||||||
|
|
|
@ -176,6 +176,15 @@ Test Summary: \e[32m2 successful\e[0m, \e[31m0 failures\e[0m, \e[37m0 skipped\e[
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'using namespaced resources' do
|
||||||
|
it 'works' do
|
||||||
|
out = inspec('exec ' + File.join(profile_path, 'dependencies', 'resource-namespace'))
|
||||||
|
out.stderr.must_equal ''
|
||||||
|
out.exit_status.must_equal 0
|
||||||
|
out.stdout.force_encoding(Encoding::UTF_8).must_include "Summary: \e[32m5 successful\e[0m, \e[31m0 failures\e[0m, \e[37m0 skipped\e[0m\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "with a 2-level dependency tree" do
|
describe "with a 2-level dependency tree" do
|
||||||
it 'correctly runs tests from the whole tree' do
|
it 'correctly runs tests from the whole tree' do
|
||||||
out = inspec('exec ' + File.join(profile_path, 'dependencies', 'inheritance'))
|
out = inspec('exec ' + File.join(profile_path, 'dependencies', 'inheritance'))
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
# copyright: 2015, The Authors
|
||||||
|
# license: All rights reserved
|
||||||
|
require_resource(profile: 'profile_c', resource: 'gordon_config', as: 'gordy_config')
|
||||||
|
|
||||||
|
describe gordy_config do
|
||||||
|
its('version') { should eq('1.0') }
|
||||||
|
end
|
||||||
|
|
||||||
|
control 'whichgordon' do
|
||||||
|
describe gordy_config do
|
||||||
|
its('version') { should eq('1.0') }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe gordon_config do
|
||||||
|
its('version') { should eq('2.0') }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe gordy_config do
|
||||||
|
its('version') { should eq(gordy_config.version) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe gordon_config do
|
||||||
|
its('version') { should eq(gordon_config.version) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
name: resource-namespace
|
||||||
|
title: InSpec Profile
|
||||||
|
maintainer: The Authors
|
||||||
|
copyright: The Authors
|
||||||
|
copyright_email: you@example.com
|
||||||
|
license: All Rights Reserved
|
||||||
|
summary: An InSpec Compliance Profile
|
||||||
|
version: 0.1.0
|
||||||
|
depends:
|
||||||
|
- name: profile_a
|
||||||
|
path: '../profile_a'
|
||||||
|
- name: profile_d
|
||||||
|
path: '../profile_d'
|
|
@ -2,7 +2,6 @@
|
||||||
# author: Steven Danna
|
# author: Steven Danna
|
||||||
|
|
||||||
require 'helper'
|
require 'helper'
|
||||||
require 'rspec/core'
|
|
||||||
require 'inspec/control_eval_context'
|
require 'inspec/control_eval_context'
|
||||||
|
|
||||||
describe Inspec::ControlEvalContext do
|
describe Inspec::ControlEvalContext do
|
||||||
|
@ -11,7 +10,6 @@ describe Inspec::ControlEvalContext do
|
||||||
"wombat"
|
"wombat"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Inspec::Log.level = :debug
|
|
||||||
|
|
||||||
let(:control_content) { <<EOF
|
let(:control_content) { <<EOF
|
||||||
control 'foo' do
|
control 'foo' do
|
||||||
|
@ -44,11 +42,36 @@ EOF
|
||||||
profile_context.stubs(:current_load).returns({file: "<test content>"})
|
profile_context.stubs(:current_load).returns({file: "<test content>"})
|
||||||
eval_context.instance_eval(control_content)
|
eval_context.instance_eval(control_content)
|
||||||
profile_context.all_rules.each do |rule|
|
profile_context.all_rules.each do |rule|
|
||||||
# Turn each rule into an example group and run it, none of the example content should raise an
|
# Turn each rule into an example group and run it, none of the
|
||||||
# exception
|
# example content should raise an exception
|
||||||
Inspec::Rule.prepare_checks(rule).each do |m, a, b|
|
Inspec::Rule.prepare_checks(rule).each do |m, a, b|
|
||||||
|
# if we require this at the top level, none of the other tests
|
||||||
|
# in this file will run. itsfine.jpg
|
||||||
|
require 'rspec/core'
|
||||||
RSpec::Core::ExampleGroup.describe(*a, &b).run
|
RSpec::Core::ExampleGroup.describe(*a, &b).run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#resource" do
|
||||||
|
let(:resource_dsl) { Inspec::Resource.create_dsl(profile_context) }
|
||||||
|
let(:inner_context) { Inspec::ProfileContext.new('inner-context', backend, {}) }
|
||||||
|
let(:control_content) do <<EOF
|
||||||
|
resource('profile_a', 'foobar')
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fails if the requested profile can't be found" do
|
||||||
|
assert_raises(Inspec::ProfileNotFound) {
|
||||||
|
eval_context.instance_eval(control_content).must_raise
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the resource from a subcontext" do
|
||||||
|
profile_context.expects(:subcontext_by_name).with('profile_a').returns(inner_context)
|
||||||
|
inner_context.expects(:resource_registry).returns({'foobar' => mock(:new => 'newfoo')})
|
||||||
|
eval_context.instance_eval(control_content).must_equal "newfoo"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue