Class: Toaster::TestManager
- Inherits:
-
Object
- Object
- Toaster::TestManager
- Defined in:
- lib/toaster/test_manager.rb
Class Method Summary collapse
- .init_db(config) ⇒ Object
- .init_test(automation_name, recipes = [], test_id = nil, prototype = "default", destroy_container = true, print_output = false) ⇒ Object
- .run_tests(test_suite, blocking = true) ⇒ Object
Instance Method Summary collapse
-
#after_run_action(task, execution_uuid, error = nil, script_output = nil) ⇒ Object
called by chef_listener AFTER a chef resource (task) has been executed by Chef’s Runner::run_action method.
-
#before_run_action(task, execution_uuid) ⇒ Object
called by chef_listener BEFORE a chef resource (task) is executed by Chef’s Runner::run_action method.
-
#initialize(config = {}) ⇒ TestManager
constructor
A new instance of TestManager.
-
#num_requested_task_executions(task) ⇒ Object
Return the number of times a given task should be executed.
- #tasks_to_repeat_now(last_executed_task) ⇒ Object
Constructor Details
#initialize(config = {}) ⇒ TestManager
Returns a new instance of TestManager.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/toaster/test_manager.rb', line 26 def initialize(config = {}) @states = {} @current_executions = {} @cookbook_paths = config["cookbook_paths"] || [] @transfer_state_config = false host = "localhost" TestManager.init_db(config) user = User.find(config["user_id"]) User.set_current_user(user) run = AutomationRun.new( :machine_id => Util.get_machine_id(), :user => user) AutomationRun.set_current(run) if config["task_execution_timeout"] ChefListener.task_execution_timeout = config["task_execution_timeout"] end if config["task_exec_timeout_repeated"] ChefListener.task_exec_timeout_repeated = config["task_exec_timeout_repeated"] end if config["rest_timeout"] Chef::Config[:rest_timeout] = config["rest_timeout"] end if !config["transfer_state_config"].nil? @transfer_state_config = config["transfer_state_config"] end # Leave this output line unchanged - it is later extracted and parsed by # test_runner.rb, which needs to be able to determine the automation run ID. # This is a bit hacky, but seemed to be the best/fastest way to solve this. puts "INFO: Current automation run ID: #{AutomationRun.get_current.id}" @automation_name = config["automation_name"] || "" @recipes = config["recipes"] || [] @skip_tasks = config["skip_tasks"] || [] @repeat_tasks = config["repeat_tasks"] || [] @repeated_tasks = [] @state_change_config = {} @state_observer_thread = nil end |
Class Method Details
.init_db(config) ⇒ Object
223 224 225 |
# File 'lib/toaster/test_manager.rb', line 223 def self.init_db(config) Config.init_db_connection(config) end |
.init_test(automation_name, recipes = [], test_id = nil, prototype = "default", destroy_container = true, print_output = false) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/toaster/test_manager.rb', line 227 def self.init_test(automation_name, recipes = [], test_id = nil, prototype="default", destroy_container=true, print_output=false) test_id = test_id || Util.generate_short_uid() suite = TestSuite.find({"uuid" => test_id}) return suite if suite && suite.size > 0 suite = TestSuite.new( :recipes => recipes, :uuid => test_id, :lxc_prototype => prototype) suite.save automation = TestRunner.ensure_automation_exists_in_db( automation_name, recipes, suite, destroy_container, print_output) if !automation puts "WARN: Could not ensure that automation '#{automation_name}' exists in DB." return nil end suite.automation = automation suite.save return suite end |
.run_tests(test_suite, blocking = true) ⇒ Object
248 249 250 251 252 253 254 |
# File 'lib/toaster/test_manager.rb', line 248 def self.run_tests(test_suite, blocking = true) if !test_suite.kind_of?(TestSuite) test_suite = TestSuite.find({"uuid" => test_suite}) end tests_to_run = test_suite.test_cases.select { |tc| !tc.executed? } TestRunner.instance.execute_tests(tests_to_run, blocking) end |
Instance Method Details
#after_run_action(task, execution_uuid, error = nil, script_output = nil) ⇒ Object
called by chef_listener AFTER a chef resource (task) has been executed by Chef’s Runner::run_action method.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/toaster/test_manager.rb', line 147 def after_run_action(task, execution_uuid, error = nil, script_output = nil) s_before = nil s_after = nil begin if !execution_uuid || !@current_executions[execution_uuid] # "init_chef_listener" is part of a special resource name which # performs the AOP based instrumentation of the Chef run if !task.resource.to_s.include?("init_chef_listener") puts "WARN: Unable to find previous state for task execution " + "UUID '#{execution_uuid}' in 'after_run_action' " + "(This may be NORMAL within the context of a Chef notification execution). " + "Currently active executions: #{@current_executions.inspect}" end return end # pause/stop monitoring add_prestate = @state_tracer.dump_execution_prestate add_state_change_config = SystemState.get_statechange_config_from_state(add_prestate) @state_tracer.stop # get additional_state_configs from automation add_automation_specific_state_config(@state_change_config) @state_change_config = ResourceInspector.get_config_for_potential_state_changes( task, @cookbook_paths, @state_change_config) # add additional state change configs from state tracer MarkupUtil.rmerge!(@state_change_config, add_state_change_config, true) if @state_change_config.empty? puts "WARN: Empty state change config for task UUID #{task.uuid}:\n#{task.sourcecode}\n------" end state = SystemState.get_system_state(@state_change_config) execution = @current_executions[execution_uuid] @current_executions.delete(execution_uuid) # add additional pre-states from state tracer MarkupUtil.rmerge!(execution.state_before, add_prestate, true) execution.end_time = TimeStamp.now().to_i execution.output = script_output execution.reduce_and_set_state_after(state) execution.success = error.nil? error = "#{error}\n" + "#{error.backtrace.join("\n")}" if error.respond_to?("backtrace") execution.error_details = error # clone the state hashes s_before = MarkupUtil.clone(execution.state_before) s_after = MarkupUtil.clone(execution.state_after) #puts "DEBUG: states before/after #{execution}: #{execution.state_before}\n/\n#{execution.state_after}" # compute state changes prop_changes = SystemState.get_state_diff(s_before, s_after) execution.state_changes = prop_changes puts "INFO: Property changes (#{prop_changes.size}): #{prop_changes.inspect}" # remove ignored properties (e.g., file modification time etc.) SystemState.remove_ignore_props!(execution.state_before) SystemState.remove_ignore_props!(execution.state_after) # mongodb does not allow special chars like "." in the JSON hash MarkupUtil.rectify_keys(execution.state_before) MarkupUtil.rectify_keys(execution.state_after) execution = execution.save rescue => ex Util.print_backtrace(ex) puts "INFO: pre-state (original): #{s_before}" puts "INFO: post-state (original): #{s_after}" puts "INFO: pre-state: #{execution.state_before}" puts "INFO: post-state: #{execution.state_after}" end end |
#before_run_action(task, execution_uuid) ⇒ Object
called by chef_listener BEFORE a chef resource (task) is executed by Chef’s Runner::run_action method.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/toaster/test_manager.rb', line 97 def before_run_action(task, execution_uuid) # indicate whether or not this task should be started/continued return false if @skip_tasks.include?(task.uuid) # clear the old state change config, if needed if !@transfer_state_config @state_change_config = {} end # get additional_state_configs from automation automation = AutomationRun.get_current().automation add_automation_specific_state_config(@state_change_config) task.automation = automation if !task.automation # determine which parameters the task code accesses: task.task_parameters.concat(ResourceInspector.get_accessed_parameters(task)) task.save if execution_uuid @state_change_config = ResourceInspector.get_config_for_potential_state_changes( task, @cookbook_paths, @state_change_config) state = SystemState.get_system_state(@state_change_config) execution = TaskExecution.new( :task => task, :state_before => state, :uuid => execution_uuid) execution.automation_run = AutomationRun.get_current() sourcecode = ChefUtil.runtime_resource_sourcecode(task.resource_obj) sourcecode.strip! if sourcecode if sourcecode != task.sourcecode execution.sourcecode = sourcecode end @current_executions[execution_uuid] = execution # use the external ptrace-based program to monitor the Chef execution # for changes in the file system (implemented via syscall hooks) if !@state_tracer @state_tracer = SyscallTracer.new() end @state_tracer.start end return true end |
#num_requested_task_executions(task) ⇒ Object
Return the number of times a given task should be executed. The result is usually 1 (default), or 0 (skip task) or 2 (repeat task once)
72 73 74 75 76 |
# File 'lib/toaster/test_manager.rb', line 72 def num_requested_task_executions(task) return 0 if @skip_tasks.include?(task.uuid) return 2 if @repeat_tasks.include?(task.uuid) return 1 end |
#tasks_to_repeat_now(last_executed_task) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/toaster/test_manager.rb', line 78 def tasks_to_repeat_now(last_executed_task) uuid = last_executed_task.kind_of?(String) ? last_executed_task : last_executed_task.uuid @repeat_tasks.each do |list| if list.kind_of?(Array) if !@repeated_tasks.include?(list) if list[-1] == uuid @repeated_tasks << list return list end end end end return nil end |