Beispiel #1
0
    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,
        })
Beispiel #2
0
    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)
Beispiel #3
0
    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'])
Beispiel #4
0
    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'])
Beispiel #5
0
    (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)
Beispiel #6
0
                    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:
Beispiel #7
0
            '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:
Beispiel #8
0
class AutoTrailValidationTests(unittest.TestCase):
    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 tearDown(self):
        self.trail_client.stop(dry_run=False)
        self.trail_client.shutdown(dry_run=False)

    def check_step_attributes(self, attribute, expected_values,
                              **status_kwargs):
        statuses = self.trail_client.status(**status_kwargs)
        self.assertNotEqual(statuses, None, 'Status returned was None.')
        self.assertNotEqual(statuses, [], 'Status returned was empty.')
        for status in statuses:
            step_name = status[StatusField.NAME]
            value = status[attribute]
            expected_value = expected_values[step_name]
            self.assertEqual(value, expected_value, (
                'The status of step {step_name} was expected to be "{expected}" but was found to be "{value}".'
            ).format(step_name=step_name, expected=expected_value,
                     value=value))

    def check_affected_steps(self, affected_steps,
                             affected_step_names_expected):
        affected_step_names = [str(step['name']) for step in affected_steps]
        self.assertEqual(len(affected_step_names),
                         len(affected_step_names_expected))
        for expected_step_name in affected_step_names_expected:
            self.assertIn(expected_step_name, affected_step_names, (
                'Expected {expected} to be affected but it was not. List of affected step names: {affected}'
            ).format(expected=expected_step_name,
                     affected=affected_step_names))

    def wait_till(self, step_name, state):
        while True:
            statuses = self.trail_client.status(fields=[StatusField.STATE])
            for status in statuses:
                if status[StatusField.NAME] == step_name and status[
                        StatusField.STATE] == state:
                    return
            sleep(0.2)

    def test_simple_run(self):
        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.READY,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.READY,
                'action_function_g': Step.READY,
            })

        affected_steps = self.trail_client.start()
        self.check_affected_steps(affected_steps, [
            'action_function_a',
            'action_function_b',
            'action_function_c',
            'action_function_d',
            'action_function_e',
            'action_function_f',
            'action_function_g',
        ])

        self.wait_till('action_function_g', Step.SUCCESS)

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.SUCCESS,
                'action_function_c': Step.SUCCESS,
                'action_function_d': Step.SUCCESS,
                'action_function_e': Step.SUCCESS,
                'action_function_f': Step.SUCCESS,
                'action_function_g': Step.SUCCESS,
            })

    def test_pause(self):
        # Pausing with dry_run should have no impact
        affected_steps = self.trail_client.pause()

        self.check_affected_steps(affected_steps, [
            'action_function_a',
            'action_function_b',
            'action_function_c',
            'action_function_d',
            'action_function_e',
            'action_function_f',
            'action_function_g',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.READY,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.READY,
                'action_function_g': Step.READY,
            })

        # Pause all steps
        affected_steps = self.trail_client.pause(dry_run=False)

        self.check_affected_steps(affected_steps, [
            'action_function_a',
            'action_function_b',
            'action_function_c',
            'action_function_d',
            'action_function_e',
            'action_function_f',
            'action_function_g',
        ])

        self.check_step_attributes(
            StatusField.STATE,
            {
                'action_function_a': Step.
                PAUSED,  # Control reaches here and pauses, so this will be the state.
                'action_function_b': Step.TOPAUSE,
                'action_function_c': Step.TOPAUSE,
                'action_function_d': Step.TOPAUSE,
                'action_function_e': Step.TOPAUSE,
                'action_function_f': Step.TOPAUSE,
                'action_function_g': Step.TOPAUSE,
            })

        affected_steps = self.trail_client.start()

        # Check whether a step in Step.TOPAUSE gets correctly transitioned to Step.PAUSED
        # Run action_function_a so that the control reaches action_function_b and action_function_e and transitions
        # them from Step.TOPAUSE to Step.PAUSED.
        affected_steps = self.trail_client.resume(name='action_function_a',
                                                  dry_run=False)
        self.check_affected_steps(affected_steps, ['action_function_a'])

        self.wait_till('action_function_b', Step.PAUSED)
        self.wait_till('action_function_e', Step.PAUSED)

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.PAUSED,
                'action_function_c': Step.TOPAUSE,
                'action_function_d': Step.TOPAUSE,
                'action_function_e': Step.PAUSED,
                'action_function_f': Step.TOPAUSE,
                'action_function_g': Step.TOPAUSE,
            })

    def test_resume(self):
        affected_steps = self.trail_client.pause(dry_run=False)
        affected_steps = self.trail_client.start()

        affected_steps = self.trail_client.resume(name='action_function_a',
                                                  dry_run=False)
        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.RUN,
                'action_function_b': Step.TOPAUSE,
                'action_function_c': Step.TOPAUSE,
                'action_function_d': Step.TOPAUSE,
                'action_function_e': Step.TOPAUSE,
                'action_function_f': Step.TOPAUSE,
                'action_function_g': Step.TOPAUSE,
            })
        self.check_affected_steps(affected_steps, ['action_function_a'])

        affected_steps = self.trail_client.resume(dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_c',
            'action_function_d',
            'action_function_e',
            'action_function_f',
            'action_function_g',
        ])

        self.wait_till('action_function_g', Step.SUCCESS)

        # Check the end state (everything succeeded)
        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.SUCCESS,
                'action_function_c': Step.SUCCESS,
                'action_function_d': Step.SUCCESS,
                'action_function_e': Step.SUCCESS,
                'action_function_f': Step.SUCCESS,
                'action_function_g': Step.SUCCESS,
            })

    def test_pause_branch_and_resume_branch(self):
        #         +----> step_b ----> step_c ----> step_d ----+
        #         |                                           |
        # step_a -|                                           +----> step_g
        #         |                                           |
        #         +----> step_e -----------------> step_f ----+
        #
        affected_steps = self.trail_client.pause_branch(
            name='action_function_b', dry_run=False)

        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_c',
            'action_function_d',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.TOPAUSE,
                'action_function_c': Step.TOPAUSE,
                'action_function_d': Step.TOPAUSE,
                'action_function_e': Step.READY,
                'action_function_f': Step.READY,
                'action_function_g': Step.READY,
            })

        affected_steps = self.trail_client.resume_branch(
            name='action_function_b', dry_run=False)

        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_c',
            'action_function_d',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.WAIT,
                'action_function_c': Step.WAIT,
                'action_function_d': Step.WAIT,
                'action_function_e': Step.READY,
                'action_function_f': Step.READY,
                'action_function_g': Step.READY,
            })

    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 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_skip(self):
        affected_steps = self.trail_client.skip(foo='bar', dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.TOSKIP,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.TOSKIP,
                'action_function_g': Step.READY,
            })

        self.trail_client.start()

        self.wait_till('action_function_g', Step.SUCCESS)

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.SKIPPED,
                'action_function_c': Step.SUCCESS,
                'action_function_d': Step.SUCCESS,
                'action_function_e': Step.SUCCESS,
                'action_function_f': Step.SKIPPED,
                'action_function_g': Step.SUCCESS,
            })

    def test_unskip(self):
        affected_steps = self.trail_client.skip(foo='bar', dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.TOSKIP,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.TOSKIP,
                'action_function_g': Step.READY,
            })

        affected_steps = self.trail_client.unskip()
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        affected_steps = self.trail_client.unskip(foo='bar', dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.WAIT,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.WAIT,
                'action_function_g': Step.READY,
            })

    def test_block(self):
        affected_steps = self.trail_client.block(foo='bar', dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.TOBLOCK,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.TOBLOCK,
                'action_function_g': Step.READY,
            })

        self.trail_client.start()

        self.wait_till('action_function_b', Step.BLOCKED)
        self.wait_till('action_function_f', Step.BLOCKED)

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.BLOCKED,
                'action_function_c': Step.WAIT,
                'action_function_d': Step.WAIT,
                'action_function_e': Step.SUCCESS,
                'action_function_f': Step.BLOCKED,
                'action_function_g': Step.WAIT,
            })

    def test_unblock(self):
        affected_steps = self.trail_client.block(foo='bar', dry_run=False)

        affected_steps = self.trail_client.unblock(foo='bar')
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        affected_steps = self.trail_client.unblock(dry_run=False)
        self.check_affected_steps(affected_steps, [
            'action_function_b',
            'action_function_f',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.READY,
                'action_function_b': Step.WAIT,
                'action_function_c': Step.READY,
                'action_function_d': Step.READY,
                'action_function_e': Step.READY,
                'action_function_f': Step.WAIT,
                'action_function_g': Step.READY,
            })

    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'])

    def test_stop(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 stop the trail with interrupt.
        affected_steps = self.trail_client.stop(dry_run=False)

        self.wait_till('long_running_action_function', Step.BLOCKED)
        self.check_affected_steps(affected_steps, [
            'long_running_action_function',
        ])

        self.check_step_attributes(
            StatusField.STATE, {
                'long_running_action_function': Step.BLOCKED,
                'action_function_b': Step.TOBLOCK,
            })

    def test_next_step(self):
        self.trail_client.pause(dry_run=False)

        self.trail_client.start()

        affected_steps = self.trail_client.next_step()

        self.check_affected_steps(affected_steps, ['action_function_a'])

        affected_steps = self.trail_client.next_step(step_count=3,
                                                     dry_run=False)

        self.check_affected_steps(affected_steps, [
            'action_function_a',
            'action_function_b',
            'action_function_e',
        ])

        self.wait_till('action_function_b', Step.SUCCESS)
        self.wait_till('action_function_e', Step.SUCCESS)
        self.wait_till('action_function_c', Step.PAUSED)
        self.wait_till('action_function_f', Step.PAUSED)

        self.check_step_attributes(
            StatusField.STATE, {
                'action_function_a': Step.SUCCESS,
                'action_function_b': Step.SUCCESS,
                'action_function_c': Step.PAUSED,
                'action_function_d': Step.TOPAUSE,
                'action_function_e': Step.SUCCESS,
                'action_function_f': Step.PAUSED,
                'action_function_g': Step.TOPAUSE,
            })
Beispiel #9
0
# 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