mirror of
https://github.com/inspec/inspec
synced 2025-02-16 22:18:38 +00:00
Events work at the unit level, except for stack hueristics
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
e3b2ec7d7c
commit
2536d76324
2 changed files with 128 additions and 19 deletions
|
@ -22,12 +22,13 @@ module Inspec
|
|||
# Each time it changes, an Input::Event is added to the #events array.
|
||||
class Event
|
||||
EVENT_PROPERTIES = [
|
||||
:action, # :create, :set, :fetch
|
||||
:provider, # Name of the plugin
|
||||
:priority, # Priority of this plugin for resolving conflicts. 1-100, higher numbers win.
|
||||
:profile, # Profile from which the input was being set
|
||||
:value, # New value, if provided.
|
||||
:file, # File containing the input-changing action, if known
|
||||
:line, # Line in file containing the input-changing action, if known
|
||||
: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
|
||||
]
|
||||
|
||||
# Value has a special handler
|
||||
|
@ -59,6 +60,16 @@ module Inspec
|
|||
def value_has_been_set?
|
||||
@value_has_been_set
|
||||
end
|
||||
|
||||
def diagnostic_string
|
||||
to_h.reject { |_, val| val.nil? }.to_a.map { |pair| "#{pair[0]}: '#{pair[1]}'" }.join(', ')
|
||||
end
|
||||
|
||||
def to_h
|
||||
EVENT_PROPERTIES.each_with_object({}) do |prop, hash|
|
||||
hash[prop] = self.send(prop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#===========================================================================#
|
||||
|
@ -157,10 +168,20 @@ module Inspec
|
|||
# the value of the input when value() is called, as well as providing a
|
||||
# debugging record of when and how the value changed.
|
||||
@events = []
|
||||
events.push make_creation_event(options)
|
||||
|
||||
update(options)
|
||||
end
|
||||
|
||||
def set_events
|
||||
events.select { |e| e.action == :set }
|
||||
end
|
||||
|
||||
def diagnostic_string
|
||||
"Attribute #{name}, with history:\n" +
|
||||
events.map(&:diagnostic_string).map { |line| " #{line}"}.join("\n")
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# Managing Value
|
||||
#--------------------------------------------------------------------------#
|
||||
|
@ -175,20 +196,38 @@ module Inspec
|
|||
normalize_type_restriction!
|
||||
|
||||
# Values are set by passing events in; but we can also infer an event.
|
||||
if options.key?(:event)
|
||||
if options.key?(:value) || options.key?(:default)
|
||||
Inspec::Log.warn "Do not provide both an Event and a value as an option to input('#{name}') - using value from event"
|
||||
if options.key?(:value) || options.key?(:default)
|
||||
if options.key?(:event)
|
||||
if options.key?(:value) || options.key?(:default)
|
||||
Inspec::Log.warn "Do not provide both an Event and a value as an option to attribute('#{name}') - using value from event"
|
||||
end
|
||||
else
|
||||
infer_event(options) # Sets options[:event]
|
||||
end
|
||||
else
|
||||
infer_event(options) # Sets option[:event]
|
||||
end
|
||||
events << options[:event]
|
||||
events << options[:event] if options.key? :event
|
||||
|
||||
enforce_type_restriction!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_creation_event(options)
|
||||
loc = options[:location] || probe_stack
|
||||
Attribute::Event.new(
|
||||
action: :create,
|
||||
provider: options[:provider],
|
||||
file: loc.path,
|
||||
line: loc.lineno,
|
||||
)
|
||||
end
|
||||
|
||||
def probe_stack
|
||||
locs = caller_locations(2,40)
|
||||
# TODO - refine huristics
|
||||
locs[0]
|
||||
end
|
||||
|
||||
# We can determine a value:
|
||||
# 1. By event.value (preferred)
|
||||
# 2. By options[:value]
|
||||
|
@ -196,8 +235,9 @@ module Inspec
|
|||
def infer_event(options)
|
||||
# Don't rely on this working; you really should be passing a proper Input::Event
|
||||
# with the context information you have.
|
||||
location = caller_locations(4,1).first # TODO check
|
||||
event = Input::Event.new(
|
||||
location = probe_stack
|
||||
event = Attribute::Event.new(
|
||||
action: :set,
|
||||
provider: :unknown,
|
||||
priority: options[:priority] || Inspec::Input::DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER,
|
||||
file: location.path,
|
||||
|
@ -239,8 +279,9 @@ module Inspec
|
|||
def value=(new_value, priority = DEFAULT_PRIORITY_FOR_VALUE_SET)
|
||||
|
||||
# Inject a new Event with the new value.
|
||||
location = caller_locations(1,1).first # TODO: check
|
||||
location = probe_stack
|
||||
events << Event.new(
|
||||
action: :set,
|
||||
provider: :value_setter,
|
||||
priority: priority,
|
||||
value: new_value,
|
||||
|
|
|
@ -2,24 +2,41 @@ require 'helper'
|
|||
require 'inspec/objects/input'
|
||||
|
||||
describe 'Inspec::Input and Events' do
|
||||
let(:ipt { Inspec::Input.new('input') }
|
||||
describe 'when creating an input' do
|
||||
it 'should have a creation event' do
|
||||
creation_events = att.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.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 }
|
||||
creation_events.count.must_equal 1
|
||||
set_events = att.set_events
|
||||
set_events.count.must_equal 1
|
||||
set_events.first.value.must_equal 42
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
it 'should add one event for each value= operation' do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'input diagnostics' do
|
||||
it 'should dump the events' do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -27,10 +44,61 @@ describe 'Inspec::Input and Events' do
|
|||
# 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
|
||||
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)
|
||||
evt2 = Inspec::Input::Event.new(value: 2, priority: 35, action: :set)
|
||||
att.update(event: evt2)
|
||||
evt3 = Inspec::Input::Event.new(value: 3, priority: 15, action: :set)
|
||||
att.update(event: evt3)
|
||||
|
||||
att.value.must_equal 2
|
||||
end
|
||||
it 'break ties using the first event of the highest priority' do
|
||||
|
||||
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)
|
||||
evt2 = Inspec::Input::Event.new(value: 2, priority: 25, action: :set)
|
||||
att.update(event: evt2)
|
||||
evt3 = Inspec::Input::Event.new(value: 3, priority: 25, action: :set)
|
||||
att.update(event: evt3)
|
||||
|
||||
att.value.must_equal 3
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: test stack hueristics?
|
||||
|
||||
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)
|
||||
evt2 = Inspec::Input::Event.new(action: :fetch, provider: :alcubierre, hit: false)
|
||||
att.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)
|
||||
|
||||
text = att.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
|
||||
|
||||
lines.each do |line|
|
||||
line.must_match /^\s\s([a-z]+:\s\'.+\',\s)*?([a-z]+:\s\'.+\')$/ # key: 'value', key: 'value' ...
|
||||
end
|
||||
|
||||
lines[0].must_include "action: 'create',"
|
||||
|
||||
lines[1].must_include "action: 'set',"
|
||||
lines[1].must_include "value: '{" # It should to_s the value
|
||||
lines[1].must_include "provider: 'unit_test'"
|
||||
lines[1].must_include "priority: '15'"
|
||||
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue