mirror of
https://github.com/inspec/inspec
synced 2024-11-22 20:53:11 +00:00
Add stack introspection
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
9faf4b1034
commit
9156a782fe
5 changed files with 86 additions and 61 deletions
|
@ -26,9 +26,9 @@ module Inspec
|
|||
:provider, # Name of the plugin
|
||||
:priority, # Priority of this plugin for resolving conflicts. 1-100, higher numbers win.
|
||||
:value, # New value, if provided.
|
||||
:file, # File containing the attribute-changing action, if known
|
||||
:line, # Line in file containing the attribute-changing action, if known
|
||||
:hit, # if action is :fetch, true if the remote source had the attribute
|
||||
:file, # File containing the input-changing action, if known
|
||||
:line, # Line in file containing the input-changing action, if known
|
||||
:hit, # if action is :fetch, true if the remote source had the input
|
||||
].freeze
|
||||
|
||||
# Value has a special handler
|
||||
|
@ -178,7 +178,7 @@ module Inspec
|
|||
end
|
||||
|
||||
def diagnostic_string
|
||||
"Attribute #{name}, with history:\n" +
|
||||
"Input #{name}, with history:\n" +
|
||||
events.map(&:diagnostic_string).map { |line| " #{line}" }.join("\n")
|
||||
end
|
||||
|
||||
|
@ -218,7 +218,7 @@ module Inspec
|
|||
|
||||
def make_creation_event(options)
|
||||
loc = options[:location] || probe_stack
|
||||
Attribute::Event.new(
|
||||
Input::Event.new(
|
||||
action: :create,
|
||||
provider: options[:provider],
|
||||
file: loc.path,
|
||||
|
@ -227,9 +227,9 @@ module Inspec
|
|||
end
|
||||
|
||||
def probe_stack
|
||||
locs = caller_locations(2, 40)
|
||||
# TODO: - refine huristics
|
||||
locs[0]
|
||||
frames = caller_locations(2, 40)
|
||||
frames.reject! { |f| f.path && f.path.include?('/lib/inspec/') }
|
||||
frames.first
|
||||
end
|
||||
|
||||
# We can determine a value:
|
||||
|
@ -240,7 +240,7 @@ module Inspec
|
|||
# Don't rely on this working; you really should be passing a proper Input::Event
|
||||
# with the context information you have.
|
||||
location = probe_stack
|
||||
event = Attribute::Event.new(
|
||||
event = Input::Event.new(
|
||||
action: :set,
|
||||
provider: :unknown,
|
||||
priority: options[:priority] || Inspec::Input::DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER,
|
||||
|
@ -302,7 +302,7 @@ module Inspec
|
|||
end
|
||||
|
||||
def has_value?
|
||||
!current_value.is_a? DEFAULT_ATTRIBUTE
|
||||
!current_value.is_a? NO_VALUE_SET
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
@ -341,7 +341,7 @@ module Inspec
|
|||
#--------------------------------------------------------------------------#
|
||||
|
||||
def to_s
|
||||
"Attribute #{name} with #{current_value}"
|
||||
"Input #{name} with #{current_value}"
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
@ -385,11 +385,11 @@ module Inspec
|
|||
end
|
||||
|
||||
if invalid_type == true
|
||||
error = Inspec::Attribute::ValidationError.new
|
||||
error.attribute_name = @name
|
||||
error.attribute_value = proposed_value
|
||||
error.attribute_type = type_req
|
||||
raise error, "Attribute '#{error.attribute_name}' with value '#{error.attribute_value}' does not validate to type '#{error.attribute_type}'."
|
||||
error = Inspec::Input::ValidationError.new
|
||||
error.input_name = @name
|
||||
error.input_value = proposed_value
|
||||
error.input_type = type_req
|
||||
raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'."
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -403,9 +403,9 @@ module Inspec
|
|||
}
|
||||
type_req = abbreviations[type_req] if abbreviations.key?(type_req)
|
||||
if !VALID_TYPES.include?(type_req)
|
||||
error = Inspec::Attribute::TypeError.new
|
||||
error.attribute_type = type_req
|
||||
raise error, "Type '#{error.attribute_type}' is not a valid attribute type."
|
||||
error = Inspec::Input::TypeError.new
|
||||
error.input_type = type_req
|
||||
raise error, "Type '#{error.input_type}' is not a valid input type."
|
||||
end
|
||||
@type = type_req
|
||||
end
|
||||
|
|
|
@ -2,88 +2,115 @@ require 'helper'
|
|||
require 'inspec/objects/input'
|
||||
|
||||
describe 'Inspec::Input and Events' do
|
||||
let(:ipt { Inspec::Input.new('input') }
|
||||
let(:ipt) { Inspec::Input.new('input') }
|
||||
|
||||
#==============================================================#
|
||||
# Create Event
|
||||
#==============================================================#
|
||||
describe 'when creating an input' do
|
||||
it 'should have a creation event' do
|
||||
creation_events = att.events.select { |e| e.action == :create }
|
||||
creation_events = ipt.events.select { |e| e.action == :create }
|
||||
creation_events.wont_be_empty
|
||||
end
|
||||
|
||||
it 'should only have a creation event if no value was provided' do
|
||||
creation_events = att.events.select { |e| e.action == :create }
|
||||
creation_events = ipt.events.select { |e| e.action == :create }
|
||||
creation_events.count.must_equal 1
|
||||
end
|
||||
|
||||
it 'should have a create and a set event if a value was provided' do
|
||||
att = Inspec::Input.new('input', value: 42)
|
||||
creation_events = att.events.select { |e| e.action == :create }
|
||||
ipt = Inspec::Input.new('input', value: 42)
|
||||
creation_events = ipt.events.select { |e| e.action == :create }
|
||||
creation_events.count.must_equal 1
|
||||
set_events = att.set_events
|
||||
set_events = ipt.set_events
|
||||
set_events.count.must_equal 1
|
||||
set_events.first.value.must_equal 42
|
||||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Set Events
|
||||
#==============================================================#
|
||||
describe 'when setting an input using value=' do
|
||||
it 'should add a set event' do
|
||||
att.set_events.count.must_equal 0
|
||||
att.value = 42
|
||||
att.set_events.count.must_equal 1
|
||||
ipt.set_events.count.must_equal 0
|
||||
ipt.value = 42
|
||||
ipt.set_events.count.must_equal 1
|
||||
end
|
||||
|
||||
it 'should add one event for each value= operation' do
|
||||
att.set_events.count.must_equal 0
|
||||
att.value = 1
|
||||
att.value = 2
|
||||
att.value = 3
|
||||
att.set_events.count.must_equal 3
|
||||
ipt.set_events.count.must_equal 0
|
||||
ipt.value = 1
|
||||
ipt.value = 2
|
||||
ipt.value = 3
|
||||
ipt.set_events.count.must_equal 3
|
||||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Picking a Winner
|
||||
#==============================================================#
|
||||
|
||||
# For more real-world testing of metadata vs --attrs vs inline, see
|
||||
# test/functional/inputs_test.rb
|
||||
describe 'priority voting' do
|
||||
it 'value() should return the correct value when there is just one set operation' do
|
||||
evt = Inspec::Input::Event.new(value: 42, priority: 25, action: :set)
|
||||
att.update(event: evt)
|
||||
att.value.must_equal 42
|
||||
ipt.update(event: evt)
|
||||
ipt.value.must_equal 42
|
||||
end
|
||||
|
||||
it 'should return the highest priority regardless of order' do
|
||||
evt1 = Inspec::Input::Event.new(value: 1, priority: 25, action: :set)
|
||||
att.update(event: evt1)
|
||||
ipt.update(event: evt1)
|
||||
evt2 = Inspec::Input::Event.new(value: 2, priority: 35, action: :set)
|
||||
att.update(event: evt2)
|
||||
ipt.update(event: evt2)
|
||||
evt3 = Inspec::Input::Event.new(value: 3, priority: 15, action: :set)
|
||||
att.update(event: evt3)
|
||||
ipt.update(event: evt3)
|
||||
|
||||
att.value.must_equal 2
|
||||
ipt.value.must_equal 2
|
||||
end
|
||||
|
||||
it 'breaks ties using the last event of the highest priority' do
|
||||
evt1 = Inspec::Input::Event.new(value: 1, priority: 15, action: :set)
|
||||
att.update(event: evt1)
|
||||
ipt.update(event: evt1)
|
||||
evt2 = Inspec::Input::Event.new(value: 2, priority: 25, action: :set)
|
||||
att.update(event: evt2)
|
||||
ipt.update(event: evt2)
|
||||
evt3 = Inspec::Input::Event.new(value: 3, priority: 25, action: :set)
|
||||
att.update(event: evt3)
|
||||
ipt.update(event: evt3)
|
||||
|
||||
att.value.must_equal 3
|
||||
ipt.value.must_equal 3
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: test stack hueristics?
|
||||
#==============================================================#
|
||||
# Stack Hueristics
|
||||
#==============================================================#
|
||||
|
||||
describe 'when determining where the call came from' do
|
||||
it 'should get the line and file correct in the constructor' do
|
||||
expected_file = __FILE__
|
||||
expected_line = __LINE__; ipt = Inspec::Input.new('some_input') # Important to keep theses on one line
|
||||
event = ipt.events.first
|
||||
event.file.must_equal expected_file
|
||||
event.line.must_equal expected_line
|
||||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Diagnostics
|
||||
#==============================================================#
|
||||
|
||||
describe 'input diagnostics' do
|
||||
it 'should dump the events' do
|
||||
evt1 = Inspec::Input::Event.new(value: {a:1, b:2}, priority: 15, action: :set, provider: :unit_test)
|
||||
att.update(event: evt1)
|
||||
ipt.update(event: evt1)
|
||||
evt2 = Inspec::Input::Event.new(action: :fetch, provider: :alcubierre, hit: false)
|
||||
att.update(event: evt2)
|
||||
ipt.update(event: evt2)
|
||||
evt3 = Inspec::Input::Event.new(value: 12, action: :set, provider: :control_dsl, file: '/tmp/some/file.rb', line: 2)
|
||||
att.update(event: evt3)
|
||||
ipt.update(event: evt3)
|
||||
|
||||
text = att.diagnostic_string
|
||||
text = ipt.diagnostic_string
|
||||
lines = text.split("\n")
|
||||
lines.count.must_equal 5 # 3 events above + 1 create + 1 input name line
|
||||
lines.shift # Not testing the inputs top line here
|
||||
|
|
|
@ -77,13 +77,8 @@ describe Inspec::InputRegistry do
|
|||
before do
|
||||
Inspec::InputRegistry.any_instance.stubs(:validate_inputs_file_readability!)
|
||||
end
|
||||
let(:profile) do
|
||||
p = mock()
|
||||
p.expects(:profile_name).returns('test_fixture_profile').at_least_once
|
||||
p
|
||||
end
|
||||
let(:seen_inputs) do
|
||||
registry.bind_profile_inputs(profile, sources)
|
||||
registry.bind_profile_inputs('test_fixture_profile', sources)
|
||||
inputs = registry.list_inputs_for_profile('test_fixture_profile')
|
||||
# Flatten Input objects down to their values
|
||||
inputs.keys.each { |input_name| inputs[input_name] = inputs[input_name].value }
|
||||
|
|
|
@ -4,7 +4,8 @@ require 'helper'
|
|||
require 'inspec/objects/input'
|
||||
|
||||
describe Inspec::Input do
|
||||
let(:input) { Inspec::Input.new('test_input') }
|
||||
let(:opts) { { } }
|
||||
let(:input) { Inspec::Input.new('test_input', opts) }
|
||||
|
||||
#==============================================================#
|
||||
# Metadata
|
||||
|
@ -73,7 +74,9 @@ describe Inspec::Input do
|
|||
}
|
||||
ipt.to_hash.must_equal expected
|
||||
end
|
||||
end #==============================================================#
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Setting Value - One Shot
|
||||
# (see events_test.rb for overwrite support)
|
||||
#==============================================================#
|
||||
|
@ -90,7 +93,7 @@ describe Inspec::Input do
|
|||
end
|
||||
|
||||
it 'the dummy value responds true to the legacy class checks' do
|
||||
input.value.is_a?(Inspec::ATTRIBUTE::DEFAULT_ATTRIBUTE).must_equal true
|
||||
input.value.is_a?(Inspec::Attribute::DEFAULT_ATTRIBUTE).must_equal true
|
||||
input.value.must_be_kind_of Inspec::Attribute::DEFAULT_ATTRIBUTE
|
||||
end
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ describe 'type validation' do
|
|||
{
|
||||
'string' => { good: 'a_string', bad: 123.3, norm: 'String' },
|
||||
'numeric' => { good: 123, bad: 'not a number', norm: 'Numeric' },
|
||||
'regex' => { good: /\d+.+/, bad: '/(.+/', norm: 'Regexp' }
|
||||
'regex' => { good: /\d+.+/, bad: '/(.+/', norm: 'Regexp' },
|
||||
'array' => { good: [1, 2, 3], bad: { a: 1, b: 2, c: 3 }, norm: 'Array' },
|
||||
'hash' => { good: { a: 1, b: 2, c: 3 }, bad: 'i am not a hash', norm: 'Hash' },
|
||||
'boolean' => { good: true, bad: 'i am not a boolean', norm: 'Boolean' },
|
||||
|
@ -84,17 +84,17 @@ describe 'type validation' do
|
|||
describe 'validate type option' do
|
||||
it 'converts regex to Regexp' do
|
||||
opts[:type] = 'regex'
|
||||
input.type_restriction.must_equal 'Regexp'
|
||||
input.type.must_equal 'Regexp'
|
||||
end
|
||||
|
||||
it 'returns the same value if there is nothing to clean' do
|
||||
opts[:type] = 'String'
|
||||
input.type_restriction.must_equal 'String'
|
||||
input.type.must_equal 'String'
|
||||
end
|
||||
|
||||
it 'returns an error if a invalid type is sent' do
|
||||
opts[:type] = 'dressing'
|
||||
ex = assert_raises(Inspec::Attribute::TypeError) { input }
|
||||
ex = assert_raises(Inspec::Input::TypeError) { input }
|
||||
ex.message.must_match /Type 'Dressing' is not a valid input type./
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue