Esempio n. 1
0
    def test_get_task(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        task_name = 'task1'
        current_task = {'id': task_name, 'name': task_name}
        expected_ctx = {'a': 123, 'b': False, '__current_task': current_task}
        task = conductor.get_task(task_name)
        self.assertEqual(task['id'], task_name)
        self.assertEqual(task['name'], task_name)
        self.assertDictEqual(task['ctx'], expected_ctx)
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        task_name = 'task2'
        current_task = {'id': task_name, 'name': task_name}
        expected_ctx = {
            'a': 123,
            'b': False,
            'c': 'xyz',
            '__current_task': current_task
        }
        task = conductor.get_task(task_name)
        self.assertEqual(task['id'], task_name)
        self.assertEqual(task['name'], task_name)
        self.assertDictEqual(task['ctx'], expected_ctx)
Esempio n. 2
0
    def test_update_task_flow(self):
        conductor = self._prep_conductor(state=states.RUNNING)

        conductor.update_task_flow('task1',
                                   events.ActionExecutionEvent(states.RUNNING))
        expected_task_flow_item = {'id': 'task1', 'state': 'running', 'ctx': 0}

        self.assertEqual(conductor._get_task_flow_idx('task1'), 0)
        self.assertDictEqual(conductor.get_task_flow_entry('task1'),
                             expected_task_flow_item)

        ac_ex_event = events.ActionExecutionEvent(states.SUCCEEDED,
                                                  result='foobar')
        conductor.update_task_flow('task1', ac_ex_event)

        expected_task_flow_item = {
            'id': 'task1',
            'state': 'succeeded',
            'task2__0': True,
            'task5__0': True,
            'ctx': 0
        }

        self.assertEqual(conductor._get_task_flow_idx('task1'), 0)
        self.assertDictEqual(conductor.get_task_flow_entry('task1'),
                             expected_task_flow_item)
Esempio n. 3
0
    def test_update_invalid_state_to_task_flow_item(self):
        conductor = self._prep_conductor(state=states.RUNNING)

        self.assertRaises(exc.InvalidState, events.ActionExecutionEvent,
                          'foobar')

        self.assertRaises(TypeError, conductor.update_task_flow, 'task1',
                          'foobar')

        # When transition is not valid, the task state is not changed. For the test case below,
        # the state change from requested to succeeded is not a valid transition.
        conductor.update_task_flow(
            'task1', events.ActionExecutionEvent(states.REQUESTED))
        expected_task_flow_item = {
            'id': 'task1',
            'state': 'requested',
            'ctx': 0
        }
        self.assertEqual(conductor._get_task_flow_idx('task1'), 0)
        self.assertDictEqual(conductor.get_task_flow_entry('task1'),
                             expected_task_flow_item)

        conductor.update_task_flow(
            'task1', events.ActionExecutionEvent(states.SUCCEEDED))
        expected_task_flow_item = {
            'id': 'task1',
            'state': 'requested',
            'ctx': 0
        }
        self.assertEqual(conductor._get_task_flow_idx('task1'), 0)
        self.assertDictEqual(conductor.get_task_flow_entry('task1'),
                             expected_task_flow_item)
Esempio n. 4
0
    def test_get_next_tasks_from_staged(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        next_task_name = 'task1'
        next_task_spec = conductor.spec.tasks.get_task(next_task_name)
        expected_ctx_val = {'a': 123, 'b': False}
        expected_task = self.format_task_item(next_task_name, expected_ctx_val,
                                              next_task_spec)
        self.assert_task_list(conductor.get_next_tasks(), [expected_task])

        for i in range(1, 5):
            task_name = 'task' + str(i)
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.RUNNING))
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.SUCCEEDED))

            next_task_name = 'task' + str(i + 1)
            next_task_spec = conductor.spec.tasks.get_task(next_task_name)
            expected_ctx_val = {'a': 123, 'b': False, 'c': 'xyz'}
            expected_task = self.format_task_item(next_task_name,
                                                  expected_ctx_val,
                                                  next_task_spec)
            self.assert_task_list(conductor.get_next_tasks(), [expected_task])
Esempio n. 5
0
    def test_get_next_tasks_when_graph_abended(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        task_name = 'task1'
        next_task_name = 'task2'
        next_task_spec = conductor.spec.tasks.get_task(next_task_name)
        ctx_value = {'a': 123, 'b': False, 'c': 'xyz'}
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))
        expected_tasks = [
            self.format_task_item(next_task_name, ctx_value, next_task_spec)
        ]
        self.assert_task_list(conductor.get_next_tasks(task_name),
                              expected_tasks)
        expected_tasks = [
            self.format_task_item(next_task_name, ctx_value, next_task_spec)
        ]
        self.assert_task_list(conductor.get_next_tasks(task_name),
                              expected_tasks)

        conductor.request_workflow_state(states.FAILED)
        self.assertListEqual(conductor.get_next_tasks(task_name), [])
Esempio n. 6
0
    def test_get_task_transition_contexts(self):
        inputs = {'a': 123, 'b': True}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        # Use task1 to get context for task2 that is staged by not yet running.
        conductor.update_task_flow('task1',
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            'task1', events.ActionExecutionEvent(states.SUCCEEDED))
        task2_in_ctx = {
            'srcs': [0],
            'value': dx.merge_dicts(copy.deepcopy(inputs), {'c': 'xyz'})
        }
        expected_contexts = {'task2__0': task2_in_ctx}
        self.assertDictEqual(conductor.get_task_transition_contexts('task1'),
                             expected_contexts)

        # Use task1 to get context for task2 that is alstaged running.
        conductor.update_task_flow('task2',
                                   events.ActionExecutionEvent(states.RUNNING))
        task2_in_ctx = {
            'srcs': [0],
            'value': dx.merge_dicts(copy.deepcopy(inputs), {'c': 'xyz'})
        }
        expected_contexts = {'task2__0': task2_in_ctx}
        self.assertDictEqual(conductor.get_task_transition_contexts('task1'),
                             expected_contexts)

        # Use task2 to get context for task3 that is not staged yet.
        self.assertDictEqual(conductor.get_task_transition_contexts('task2'),
                             {})

        # Use task3 that is not yet staged to get context.
        self.assertRaises(exc.InvalidTaskFlowEntry,
                          conductor.get_task_transition_contexts, 'task3')
    def test_task_transition_publish_seq_ref_error(self):
        wf_def = """
        version: 1.0

        description: A basic branching workflow.

        vars:
          - foobar: fubar

        tasks:
          task1:
            action: core.noop
            next:
              - when: <% succeeded() %>
                publish:
                  - x: 123
                  - y: <% ctx().x %>
                  - z: <% ctx().y.value %>
                do: task2
          task2:
            action: core.noop
            next:
              - when: <% succeeded() %>
                publish:
                  - var2: 123
        """

        expected_errors = [
            {
                'type': 'error',
                'message': (
                    'YaqlEvaluationException: Unable to evaluate expression '
                    '\'<% ctx().y.value %>\'. NoFunctionRegisteredException: '
                    'Unknown function "#property#value"'
                ),
                'task_id': 'task1',
                'task_transition_id': 'task2__0'
            }
        ]

        spec = specs.WorkflowSpec(wf_def)
        self.assertDictEqual(spec.inspect(), {})

        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_state(states.RUNNING)

        # The workflow should fail on completion of task1 while evaluating task transition.
        task_name = 'task1'
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        # The workflow should fail with the expected errors.
        self.assertEqual(conductor.get_workflow_state(), states.FAILED)
        actual_errors = sorted(conductor.errors, key=lambda x: x.get('task_id', None))
        self.assertListEqual(actual_errors, expected_errors)
        self.assertNotIn('task2', conductor.flow.staged)
        self.assertListEqual(conductor.get_next_tasks(), [])
Esempio n. 8
0
    def test_set_workflow_paused_when_no_active_tasks(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        task_name = 'task1'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))
        conductor.request_workflow_state(states.PAUSED)
        self.assertEqual(conductor.get_workflow_state(), states.PAUSED)
    def test_runtime_function_of_graph_size(self):
        num_tasks = 100

        conductor = self._prep_conductor(num_tasks, state=states.RUNNING)

        for i in range(1, num_tasks + 1):
            task_name = 't' + str(i)
            conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.RUNNING))
            conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        self.assertEqual(conductor.get_workflow_state(), states.SUCCEEDED)
Esempio n. 10
0
    def test_get_workflow_output_when_workflow_incomplete(self):
        inputs = {'a': 123, 'b': True}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        for i in range(1, 5):
            task_name = 'task' + str(i)
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.RUNNING))
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        self.assertEqual(conductor.get_workflow_state(), states.RUNNING)
        self.assertIsNone(conductor.get_workflow_output())
Esempio n. 11
0
    def test_init_task_with_no_action(self):
        wf_def = """
        version: 1.0

        tasks:
          task1:
            next:
              - publish: xyz=123
                do: task2
          task2:
            action: core.noop
        """

        spec = specs.WorkflowSpec(wf_def)
        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_state(states.RUNNING)

        # Process task1.
        next_task_name = 'task1'
        next_task_spec = conductor.spec.tasks.get_task(next_task_name)
        expected_ctx_value = {}
        expected_tasks = [
            self.format_task_item(next_task_name, expected_ctx_value,
                                  next_task_spec)
        ]
        self.assert_task_list(conductor.get_next_tasks(), expected_tasks)

        task_name = 'task1'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        # Process task2.
        next_task_name = 'task2'
        next_task_spec = conductor.spec.tasks.get_task(next_task_name)
        expected_ctx_value = {'xyz': 123}
        expected_tasks = [
            self.format_task_item(next_task_name, expected_ctx_value,
                                  next_task_spec)
        ]
        self.assert_task_list(conductor.get_next_tasks(task_name),
                              expected_tasks)

        task_name = 'task2'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        self.assertEqual(conductor.get_workflow_state(), states.SUCCEEDED)
    def test_get_next_tasks_with_task_input_error(self):
        wf_def = """
        version: 1.0

        description: A basic branching workflow.

        vars:
          - foobar: fubar

        tasks:
          task1:
            action: core.noop
            next:
              - when: <% succeeded() %>
                do: task2
          task2:
            action: core.noop
            input:
              var_x: <% ctx().foobar.fubar %>
        """

        expected_errors = [
            {
                'type': 'error',
                'message': (
                    'YaqlEvaluationException: Unable to evaluate expression '
                    '\'<% ctx().foobar.fubar %>\'. NoFunctionRegisteredException: '
                    'Unknown function "#property#fubar"'
                ),
                'task_id': 'task2'
            }
        ]

        spec = specs.WorkflowSpec(wf_def)
        self.assertDictEqual(spec.inspect(), {})

        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_state(states.RUNNING)

        # Manually complete task1.
        task_name = 'task1'
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        # The get_next_tasks method should not return any tasks.
        self.assertListEqual(conductor.get_next_tasks(), [])

        # The workflow should fail with the expected errors.
        self.assertEqual(conductor.get_workflow_state(), states.FAILED)
        actual_errors = sorted(conductor.errors, key=lambda x: x.get('task_id', None))
        self.assertListEqual(actual_errors, expected_errors)
Esempio n. 13
0
def request_next_tasks(task_ex_id):
    task_ex_db = wf_db_access.TaskExecution.get_by_id(task_ex_id)

    # Return if task execution is not complete..
    if task_ex_db.status not in states.COMPLETED_STATES:
        return

    # Update task flow if task execution is completed.
    conductor, wf_ex_db = refresh_conductor(task_ex_db.workflow_execution)
    ac_ex_event = events.ActionExecutionEvent(task_ex_db.status,
                                              result=task_ex_db.result)
    conductor.update_task_flow(task_ex_db.task_id, ac_ex_event)

    # Identify the list of next set of tasks.
    next_tasks = conductor.get_next_tasks(task_ex_db.task_id)

    # If there is no new tasks, update execution records to handle possible completion.
    if not next_tasks:
        # Update workflow execution and related liveaction and action execution.
        update_execution_records(wf_ex_db, conductor)

    # Iterate while there are next tasks identified for processing. In the case for
    # task with no action execution defined, the task execution will complete
    # immediately with a new set of tasks available.
    while next_tasks:
        # Mark the tasks as running in the task flow before actual task execution.
        for task in next_tasks:
            ac_ex_event = events.ActionExecutionEvent(states.RUNNING)
            conductor.update_task_flow(task['id'], ac_ex_event)

        # Update workflow execution and related liveaction and action execution.
        update_execution_records(wf_ex_db, conductor)

        # Request task execution for the tasks.
        for task in next_tasks:
            try:
                task_id, task_spec, task_ctx = task['id'], task['spec'], task[
                    'ctx']
                st2_ctx = {'execution_id': wf_ex_db.action_execution}
                request_task_execution(wf_ex_db, task_id, task_spec, task_ctx,
                                       st2_ctx)
            except Exception as e:
                fail_workflow_execution(str(wf_ex_db.id),
                                        e,
                                        task_id=task['id'])
                return

        # Identify the next set of tasks to execute.
        conductor, wf_ex_db = refresh_conductor(str(wf_ex_db.id))
        next_tasks = conductor.get_next_tasks()
Esempio n. 14
0
    def test_get_workflow_output_when_workflow_succeeded(self):
        inputs = {'a': 123, 'b': True}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        for i in range(1, 6):
            task_name = 'task' + str(i)
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.RUNNING))
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        expected_output = {'data': {'a': 123, 'b': True, 'c': 'xyz'}}
        self.assertEqual(conductor.get_workflow_state(), states.SUCCEEDED)
        self.assertDictEqual(conductor.get_workflow_output(), expected_output)
    def assert_data_flow(self, input_value):
        inputs = {'a1': input_value}
        expected_output = {'a5': inputs['a1'], 'b5': inputs['a1']}

        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        for i in range(1, len(conductor.spec.tasks) + 1):
            task_name = 'task' + str(i)
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.RUNNING))
            conductor.update_task_flow(
                task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        self.assertEqual(conductor.get_workflow_state(), states.SUCCEEDED)
        self.assertDictEqual(conductor.get_workflow_output(), expected_output)
Esempio n. 16
0
def update_task_state(task_ex_id,
                      ac_ex_status,
                      ac_ex_result=None,
                      ac_ex_ctx=None,
                      publish=True):
    # Return if action execution status is not in the list of statuses to process.
    statuses_to_process = (copy.copy(ac_const.LIVEACTION_COMPLETED_STATES) + [
        ac_const.LIVEACTION_STATUS_PAUSED, ac_const.LIVEACTION_STATUS_PENDING
    ])

    if ac_ex_status not in statuses_to_process:
        return

    # Refresh records
    task_ex_db = wf_db_access.TaskExecution.get_by_id(task_ex_id)
    conductor, wf_ex_db = refresh_conductor(task_ex_db.workflow_execution)
    wf_ac_ex_id = wf_ex_db.action_execution

    # Update task flow if task execution is completed or paused.
    msg = '[%s] Publish task "%s", route "%s", with status "%s" to conductor.'
    LOG.info(msg, wf_ac_ex_id, task_ex_db.task_id, str(task_ex_db.task_route),
             task_ex_db.status)
    ac_ex_event = events.ActionExecutionEvent(ac_ex_status,
                                              result=ac_ex_result,
                                              context=ac_ex_ctx)
    LOG.debug('[%s] %s', wf_ac_ex_id, conductor.serialize())
    conductor.update_task_state(task_ex_db.task_id, task_ex_db.task_route,
                                ac_ex_event)

    # Update workflow execution and related liveaction and action execution.
    update_execution_records(wf_ex_db,
                             conductor,
                             update_lv_ac_on_statuses=statuses_to_process,
                             pub_lv_ac=publish,
                             pub_ac_ex=publish)
Esempio n. 17
0
    def assert_workflow_state(self,
                              wf_name,
                              mock_flow,
                              expected_wf_states,
                              conductor=None):
        if not conductor:
            wf_def = self.get_wf_def(wf_name)
            wf_spec = self.spec_module.instantiate(wf_def)
            conductor = conducting.WorkflowConductor(wf_spec)
            conductor.request_workflow_state(states.RUNNING)

        for task_flow_entry, expected_wf_state in zip(mock_flow,
                                                      expected_wf_states):
            task_id = task_flow_entry['id']
            task_state = task_flow_entry['state']
            ac_ex_event = events.ActionExecutionEvent(task_state)
            conductor.update_task_flow(task_id, ac_ex_event)

            err_ctx = ('Workflow state "%s" is not the expected state "%s". '
                       'Updated task "%s" with state "%s".' %
                       (conductor.get_workflow_state(), expected_wf_state,
                        task_id, task_state))

            self.assertEqual(conductor.get_workflow_state(), expected_wf_state,
                             err_ctx)

        return conductor
Esempio n. 18
0
    def forward_task_statuses(self,
                              conductor,
                              task_id,
                              statuses,
                              item_ids=None,
                              results=None,
                              accumulated_results=None,
                              route=0):
        if item_ids is None:
            item_ids = []

        if results is None:
            results = []

        if accumulated_results is None:
            accumulated_results = []

        status_changes = six.moves.zip_longest(statuses, item_ids, results,
                                               accumulated_results)

        for status, item_id, result, accumulated_result in status_changes:
            ac_ex_event = (events.ActionExecutionEvent(status)
                           if item_id is None or item_id < 0 else
                           events.TaskItemActionExecutionEvent(
                               item_id, status))

            if item_id and item_id >= 0 and accumulated_result is not None:
                ac_ex_event.accumulated_result = accumulated_result

            if result:
                ac_ex_event.result = result

            conductor.update_task_state(task_id, route, ac_ex_event)
Esempio n. 19
0
    def test_bad_current_task_state(self):
        task_flow_entry = {'id': 'task1', 'ctx': 0, 'state': 'mock'}
        ac_ex_event = events.ActionExecutionEvent(states.SUCCEEDED)

        self.assertRaises(exc.InvalidTaskStateTransition,
                          machines.TaskStateMachine.process_event, None,
                          task_flow_entry, ac_ex_event)
Esempio n. 20
0
    def prep_wf_ex(self, wf_ex_db):
        data = {
            'spec': wf_ex_db.spec,
            'graph': wf_ex_db.graph,
            'state': wf_ex_db.status,
            'flow': wf_ex_db.flow,
            'context': wf_ex_db.context,
            'input': wf_ex_db.input,
            'output': wf_ex_db.output,
            'errors': wf_ex_db.errors
        }

        conductor = conducting.WorkflowConductor.deserialize(data)
        conductor.request_workflow_state(wf_lib_states.RUNNING)

        for task in conductor.get_next_tasks():
            ac_ex_event = events.ActionExecutionEvent(wf_lib_states.RUNNING)
            conductor.update_task_flow(task['id'], ac_ex_event)

        wf_ex_db.status = conductor.get_workflow_state()
        wf_ex_db.flow = conductor.flow.serialize()
        wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db,
                                                         publish=False)

        return wf_ex_db
Esempio n. 21
0
    def forward_task_statuses(self, conductor, task_id, status_changes, route=0, result=None):
        for status in status_changes:
            ac_ex_event = events.ActionExecutionEvent(status)

            if result is not None and status in statuses.COMPLETED_STATUSES:
                ac_ex_event.result = result

            conductor.update_task_state(task_id, route, ac_ex_event)
Esempio n. 22
0
    def test_app_ctx_references(self):
        app_ctx = {'x': 'foobar', 'y': 'fubar', 'z': 'phobar'}

        wf_def = """
        version: 1.0

        input:
          - a: <% ctx().x %>

        vars:
          - b: <% ctx().y %>

        output:
          - x: <% ctx().a %>
          - y: <% ctx().b %>
          - z: <% ctx().z %>

        tasks:
          task1:
            action: core.noop
        """

        expected_output = app_ctx
        expected_errors = []

        spec = specs.WorkflowSpec(wf_def)
        self.assertDictEqual(spec.inspect(app_ctx=app_ctx), {})

        # Run the workflow.
        conductor = conducting.WorkflowConductor(spec, context=app_ctx)
        conductor.request_workflow_state(states.RUNNING)
        self.assertEqual(conductor.get_workflow_state(), states.RUNNING)
        self.assertListEqual(conductor.errors, expected_errors)

        # Complete tasks
        task_name = 'task1'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        # Check workflow status and output.
        self.assertEqual(conductor.get_workflow_state(), states.SUCCEEDED)
        self.assertListEqual(conductor.errors, expected_errors)
        self.assertDictEqual(conductor.get_workflow_output(), expected_output)
Esempio n. 23
0
    def test_get_next_tasks_repeat_by_task_name(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)
        self.assertEqual(len(conductor.get_next_tasks()), 1)

        conductor.update_task_flow('task1',
                                   events.ActionExecutionEvent(states.RUNNING))
        self.assertEqual(len(conductor.get_next_tasks('task1')), 0)

        conductor.update_task_flow(
            'task1', events.ActionExecutionEvent(states.SUCCEEDED))
        next_tasks = conductor.get_next_tasks('task1')
        self.assertEqual(len(next_tasks), 1)
        self.assertEqual(next_tasks[0]['name'], 'task2')

        conductor.update_task_flow('task2',
                                   events.ActionExecutionEvent(states.RUNNING))
        self.assertEqual(len(conductor.get_next_tasks('task2')), 0)
Esempio n. 24
0
    def test_set_workflow_canceled_when_has_active_tasks(self):
        inputs = {'a': 123}
        conductor = self._prep_conductor(inputs=inputs, state=states.RUNNING)

        task_name = 'task1'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))
        conductor.request_workflow_state(states.CANCELED)
        self.assertEqual(conductor.get_workflow_state(), states.CANCELING)
    def test_workflow_output_seq_ref_error(self):
        wf_def = """
        version: 1.0

        output:
          - x: 123
          - y: <% ctx().x %>
          - z: <% ctx().y.value %>

        tasks:
          task1:
            action: core.noop
        """

        expected_output = {
            'x': 123,
            'y': 123
        }

        expected_errors = [
            {
                'type': 'error',
                'message': (
                    'YaqlEvaluationException: Unable to evaluate expression '
                    '\'<% ctx().y.value %>\'. NoFunctionRegisteredException: '
                    'Unknown function "#property#value"'
                )
            }
        ]

        spec = specs.WorkflowSpec(wf_def)
        self.assertDictEqual(spec.inspect(), {})

        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_state(states.RUNNING)

        # Manually complete task1.
        task_name = 'task1'
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.RUNNING))
        conductor.update_task_flow(task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        self.assertEqual(conductor.get_workflow_state(), states.FAILED)
        self.assertListEqual(conductor.errors, expected_errors)
        self.assertDictEqual(conductor.get_workflow_output(), expected_output)
Esempio n. 26
0
    def test_retry_given_condition_not_satisfied(self):
        wf_def = """
        version: 1.0

        description: A basic sequential workflow.

        tasks:
          task1:
            action: core.noop
            retry:
              when: <% result().status_code = 400 %>
              count: 3
            next:
              - when: <% succeeded() %>
                do: task2
          task2:
            action: core.noop
        """

        # Setup workflow conductor.
        spec = native_specs.WorkflowSpec(wf_def)
        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_status(statuses.RUNNING)

        # Forward task1 from running to succeeded.
        task_id = "task1"
        route = 0
        task_result = {"status_code": 200}
        ac_ex_event = events.ActionExecutionEvent(statuses.RUNNING)
        conductor.update_task_state(task_id, route, ac_ex_event)
        ac_ex_event = events.ActionExecutionEvent(statuses.SUCCEEDED,
                                                  result=task_result)
        conductor.update_task_state(task_id, route, ac_ex_event)

        # Get task state for task1.
        task_state_entry = conductor.get_task_state_entry(task_id, route)

        # Set context for task1.
        current_task_ctx = conductor.make_task_context(task_state_entry,
                                                       task_result=task_result)

        # Assert retry is false for task1.
        self.assertFalse(
            conductor._evaluate_task_retry(task_state_entry, current_task_ctx))
Esempio n. 27
0
    def test_workflow_output_with_error(self):
        wf_def = """
        version: 1.0

        output:
          - x: 123
          - y: <% ctx().x %>
          - z: <% ctx().y.value %>

        tasks:
          task1:
            action: core.noop
        """

        expected_output = {'x': 123, 'y': 123}

        expected_errors = [{
            'type':
            'error',
            'message':
            ('YaqlEvaluationException: Unable to evaluate expression '
             '\'<% ctx().y.value %>\'. NoFunctionRegisteredException: '
             'Unknown function "#property#value"')
        }]

        spec = specs.WorkflowSpec(wf_def)
        self.assertDictEqual(spec.inspect(), {})

        # Run the workflow and keep it running.
        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_state(states.RUNNING)
        task_name = 'task1'
        conductor.update_task_flow(task_name,
                                   events.ActionExecutionEvent(states.RUNNING))

        # Cancels the workflow and complete task1.
        conductor.request_workflow_state(states.CANCELING)
        conductor.update_task_flow(
            task_name, events.ActionExecutionEvent(states.SUCCEEDED))

        # Check workflow status is not changed to failed given the output error.
        self.assertEqual(conductor.get_workflow_state(), states.CANCELED)
        self.assertListEqual(conductor.errors, expected_errors)
        self.assertDictEqual(conductor.get_workflow_output(), expected_output)
Esempio n. 28
0
    def test_retry_default_condition_satisfied(self):
        wf_def = """
        version: 1.0

        description: A basic sequential workflow.

        tasks:
          task1:
            action: core.noop
            retry:
              count: 3
            next:
              - when: <% succeeded() %>
                do: task2
          task2:
            action: core.noop
        """

        # Setup workflow conductor.
        spec = native_specs.WorkflowSpec(wf_def)
        conductor = conducting.WorkflowConductor(spec)
        conductor.request_workflow_status(statuses.RUNNING)

        # Forward task1 from running to failed.
        task_id = "task1"
        route = 0
        ac_ex_event = events.ActionExecutionEvent(statuses.RUNNING)
        conductor.update_task_state(task_id, route, ac_ex_event)
        ac_ex_event = events.ActionExecutionEvent(statuses.FAILED)
        conductor.update_task_state(task_id, route, ac_ex_event)

        # Get task state for task1.
        task_state_entry = conductor.get_task_state_entry(task_id, route)

        # Mock the task status.
        task_state_entry["status"] = statuses.FAILED

        # Set context for task1.
        current_task_ctx = conductor.make_task_context(task_state_entry,
                                                       task_result=None)

        # Assert retry is true for task1.
        self.assertTrue(
            conductor._evaluate_task_retry(task_state_entry, current_task_ctx))
Esempio n. 29
0
    def test_update_task_state_for_not_ready_task(self):
        conductor = self._prep_conductor(status=statuses.RUNNING)

        self.assertRaises(
            exc.InvalidTaskStateEntry,
            conductor.update_task_state,
            "task2",
            0,
            events.ActionExecutionEvent(statuses.RUNNING),
        )
Esempio n. 30
0
    def test_update_task_state_for_nonexistent_task(self):
        conductor = self._prep_conductor(status=statuses.RUNNING)

        self.assertRaises(
            exc.InvalidTask,
            conductor.update_task_state,
            "task999",
            0,
            events.ActionExecutionEvent(statuses.RUNNING),
        )