Пример #1
0
    True      -- if the step has succeeded.
    False     -- otherwise.
    """
    # To understand the structure of the result returned by the API calls, please see the documentation of the
    # TrailClient class.
    statuses = client.status(fields=[StatusField.STATE], name=step_name)

    # In this case, the status call returns a list of step statuses.
    # Since we have exactly one step with each name and we are querying the status of steps with the given name,
    # there will be only one element in the result list. Hence we refer to the zeroth element of results.
    if statuses and statuses[0][StatusField.STATE] == Step.SUCCESS:
        return True
    return False


# Run the trail
# Since this is an interactive client, it will print the result of the API call to STDOUT.
day_trail_interactive_client.start()
print '\n\n'

# Print the status every 2 seconds while the trail is running.
while not is_step_done(day_trail_client, 'end_the_day'):
    print '=========================== Status of the trail ==========================='
    day_trail_interactive_client.status()
    print '===========================================================================\n\n'
    sleep(2)

# We have reached the end state we want and we need to shutdown the trail server.
day_trail_client.shutdown(dry_run=False)
print '[Example Trail] -- Shutdown the trail server. Example ends.'
Пример #2
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,
            })