def test_pause_on_fail(self): # First shutdown the default setup trail. self.trail_client.shutdown(dry_run=False) def failing_action_function(trail_env, context): raise Exception('mock failure') step_a = Step(failing_action_function) step_b = Step(action_function_b) # Trail definition: The following example trail represents the following DAG: # step_a ----> step_b test_trail_definition = [(step_a, step_b)] context = Context(42) self.trail_server = TrailServer(test_trail_definition, delay=0.1, context=context) self.trail_server.serve(threaded=True) self.trail_client = TrailClient(self.trail_server.socket_file) self.trail_client.start() self.wait_till('failing_action_function', Step.PAUSED_ON_FAIL) self.check_step_attributes( StatusField.STATE, { 'failing_action_function': Step.PAUSED_ON_FAIL, 'action_function_b': Step.WAIT, }) self.check_step_attributes(StatusField.RETURN_VALUE, { 'failing_action_function': 'mock failure', 'action_function_b': None, })
def setUp(self): self.step_a = Step(action_function_a) self.step_b = Step(action_function_b, foo='bar') self.step_c = Step(action_function_c) self.step_d = Step(action_function_d) self.step_e = Step(action_function_e) self.step_f = Step(action_function_f, foo='bar') self.step_g = Step(action_function_g) # Trail definition # The following example trail represents the following DAG: # The run-time (in seconds) of each step is mentioned below the step name. # +----> step_b ----> step_c ----> step_d ----+ # | 1 1 1 | # step_a -| +----> step_g # 1 | 1 1 | 1 # +----> step_e -----------------> step_f ----+ # test_trail_definition = [ (self.step_a, self.step_b), (self.step_a, self.step_e), # First branch (self.step_b, self.step_c), (self.step_c, self.step_d), (self.step_d, self.step_g), # Second branch (self.step_e, self.step_f), (self.step_f, self.step_g), ] # Adding this for convenience in tests. self.step_list = [ self.step_a, self.step_b, self.step_c, self.step_d, self.step_e, self.step_f, self.step_g, ] # Setup trail to run automatically (non-interactively) context = Context(42) self.trail_server = TrailServer(test_trail_definition, delay=0.1, context=context) self.trail_server.serve(threaded=True) self.trail_client = TrailClient(self.trail_server.socket_file)
def test_message_passing(self): # First shutdown the default setup trail. self.trail_client.shutdown(dry_run=False) def io_action_function(trail_env, context): reply = trail_env.input('Prompting user') trail_env.output('Reply received: {}'.format(reply)) def slow_action_function(trail_env, context): sleep(0.2) step_a = Step(io_action_function) step_b = Step(slow_action_function) # Trail definition: The following example trail represents the following DAG: # step_a ----> step_b # test_trail_definition = [(step_a, step_b)] context = Context(42) self.trail_server = TrailServer(test_trail_definition, delay=0.1, context=context) self.trail_server.serve(threaded=True) self.trail_client = TrailClient(self.trail_server.socket_file) self.trail_client.start() self.wait_till('io_action_function', Step.RUN) statuses = self.trail_client.status(name='io_action_function') self.assertEqual(len(statuses), 1) prompt_message = statuses[0][StatusField.UNREPLIED_PROMPT_MESSAGE] self.assertEqual(prompt_message, 'Prompting user') # Reply to steps self.trail_client.send_message_to_steps(name='io_action_function', message='Done', dry_run=False) self.wait_till('io_action_function', Step.SUCCESS) # Check reply statuses = self.trail_client.status( name='io_action_function', fields=[StatusField.OUTPUT_MESSAGES]) self.assertEqual(len(statuses), 1) output_messages = statuses[0][StatusField.OUTPUT_MESSAGES] self.assertEqual(output_messages, ['Reply received: Done'])
def test_interrupt(self): # First shutdown the default setup trail. self.trail_client.shutdown(dry_run=False) # We need a long running step to be able to invoke interrupt on it. def long_running_action_function(trail_env, context): sleep(3) return "This is action A. Value from context: {}".format( context.value) step_a = Step(long_running_action_function) step_b = Step(action_function_b) # Define a trail with slightly long running steps. # The following example trail represents the following DAG: # step_a ----> step_b test_trail_definition = [(step_a, step_b)] context = Context(42) self.trail_server = TrailServer(test_trail_definition, delay=0.1, context=context) self.trail_server.serve(threaded=True) self.trail_client = TrailClient(self.trail_server.socket_file) self.trail_client.start() self.wait_till('long_running_action_function', Step.RUN) # long_running_action_function is a long running step (5 seconds), so terminate it. affected_steps = self.trail_client.interrupt(dry_run=False) self.check_affected_steps(affected_steps, ['long_running_action_function']) self.check_step_attributes( StatusField.STATE, { 'long_running_action_function': Step.INTERRUPTED, 'action_function_b': Step.WAIT, }) # Make sure interrupted is a state that can be resumed from (re-run). affected_steps = self.trail_client.resume() self.check_affected_steps(affected_steps, ['long_running_action_function'])
(step_a, step_b), (step_a, step_e), # First branch (step_b, step_c), (step_c, step_d), (step_d, step_g), # Second branch (step_e, step_f), (step_f, step_g), ] # Setup trail to run automatically (non-interactively) context = Context(42) example_trail_server = TrailServer(example_trail_definition, delay=0.5, context=context) # Run the server in a separate thread so that we can interact with it right here. example_trail_server.serve(threaded=True) example_trail = TrailClient(example_trail_server.socket_file) example = InteractiveTrail(example_trail) # Start interactive session interactive(globals(), prompt='Example Trail>') example.stop(interrupt=True) example.shutdown(dry_run=False)
help='The factor to use for computing sleep time.') branch_selector = parser.add_mutually_exclusive_group(required=True) branch_selector.add_argument('--lazy', dest='branch_choice', action='store_const', const=BranchChoice.lazy) branch_selector.add_argument('--hardwork', dest='branch_choice', action='store_const', const=BranchChoice.hardwork) args = parser.parse_args() # Setup trail server context = Context(args.branch_choice, factor=args.factor) day_trail_server = TrailServer(my_day_trail_definition, delay=0.5, context=context) # Since we will be interacting (using a client) with the trail in the same script, we need to run it as a separate # process. day_trail_server.serve(threaded=True) # Here we use 2 types of clients to interact with the trail. # 1. A programmatic client, which returns dictionaries/lists containing API results. day_trail_client = TrailClient(day_trail_server.socket_file) # 2. An interactive client, which prints the API results to STDOUT. # This client can easily be made to write the API results to a file or any other stream. Please read the # documentation of the class to know the full functionality. day_trail_interactive_client = InteractiveTrail(day_trail_client) # To start an interactive session, uncomment the following:
'List the files changed last with: ls -ltr'))) # Trail definition # |--> step_c --> step_e # step_a --> step_b -->| # |--> step_d # # runbook_trail_definition = [ (step_a, step_b), (step_b, step_c), (step_b, step_d), (step_c, step_e), ] runbook_trail_server = TrailServer(runbook_trail_definition, delay=0.1) runbook_trail_server.serve(threaded=True) runbook_trail = TrailClient(runbook_trail_server.socket_file) runbook_interactive = InteractiveTrail(runbook_trail) runbook_trail.start() # Convenience functions to save typing def runbook(n=None): """This is the only function the user needs to use for the normal run of the runbook. Usage: runbook() -- Start running the runbook trail if it isn't running. runbook() -- Get next instruction. This will show all the instructions that are:
# step_a -| +----> step_g # 5 | 5 1 | 10 # +----> step_e -----------------> step_f ----+ # example_trail_definition = [ (step_a, step_b), (step_a, step_e), # First branch (step_b, step_c), (step_c, step_d), (step_d, step_g), # Second branch (step_e, step_f), (step_f, step_g), ] # Setup trail to run automatically (non-interactively) context = Context(42) socket_file = os.path.join('/', 'tmp', 'autotrail.example.server') example_trail_server = TrailServer(example_trail_definition, socket_file=socket_file, delay=0.5, context=context) try: example_trail_server.serve() except: os.remove(socket_file) raise