Exemplo n.º 1
0
    def test_task_raises_exception_ignore(self, mock_class):
        """ A flow continues when a task configured with ignore_failure raises an exception """

        flow_config = FlowConfig({
            "description": "Run a task",
            "steps": {
                1: {
                    "task": "raise_exception",
                    "ignore_failure": True
                },
                2: {
                    "task": "pass_name"
                },
            },
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()
        self.assertEquals(2, len(flow.steps))
Exemplo n.º 2
0
 def test_run__nested_flow(self):
     """ Flows can run inside other flows """
     flow_config = FlowConfig({
         "description": "Run a task and a flow",
         "steps": {
             1: {
                 "task": "pass_name"
             },
             2: {
                 "flow": "nested_flow"
             }
         },
     })
     flow = FlowCoordinator(self.project_config, flow_config)
     flow.run(self.org_config)
     self.assertEqual(2, len(flow.steps))
     self.assertEqual(flow.results[0].return_values,
                      flow.results[1].return_values)
Exemplo n.º 3
0
    def test_flow_no_org_no_org_id(self, mock_class):
        """ A flow without an org does not print the org ID """

        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "task": "pass_name"
                },
                2: {
                    "task": "pass_name"
                }
            },
        })
        flow = BaseFlow(self.project_config, flow_config, None)
        flow()

        self.assertFalse(any(ORG_ID in s for s in self.flow_log["info"]))
Exemplo n.º 4
0
 def test_nested_flow(self, mock_class):
     """ Flows can run inside other flows """
     flow_config = FlowConfig({
         "description": "Run a task and a flow",
         "steps": {
             1: {
                 "task": "pass_name"
             },
             2: {
                 "flow": "nested_flow"
             }
         },
     })
     flow = BaseFlow(self.project_config, flow_config, self.org_config)
     flow()
     self.assertEqual(2, len(flow.steps))
     self.assertEqual(flow.step_return_values[0],
                      flow.step_return_values[1][0])
Exemplo n.º 5
0
    def test_run__one_task(self):
        """ A flow with one task will execute the task """
        flow_config = FlowConfig({
            "description": "Run one task",
            "steps": {
                1: {
                    "task": "pass_name"
                }
            }
        })
        flow = FlowCoordinator(self.project_config, flow_config)
        self.assertEqual(1, len(flow.steps))

        flow.run(self.org_config)

        self.assertTrue(
            any(flow_config.description in s for s in self.flow_log["info"]))
        self.assertEqual({"name": "supername"}, flow.results[0].return_values)
Exemplo n.º 6
0
    def test_flow_no_org_no_org_id(self, mock_class):
        """ A flow without an org does not print the org ID """

        flow_config = FlowConfig({
            'description': 'Run two tasks',
            'tasks': {
                1: {
                    'task': 'pass_name'
                },
                2: {
                    'task': 'pass_name'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, None)
        flow()

        self.assertFalse(any(ORG_ID in s for s in self.flow_log['info']))
    def test_run__nested_option_backrefs(self):
        flow_config = FlowConfig(
            {
                "description": "Run two tasks",
                "steps": {
                    1: {"flow": "nested_flow"},
                    2: {
                        "task": "name_response",
                        "options": {"response": "^^nested_flow.pass_name.name"},
                    },
                },
            }
        )

        flow = FlowCoordinator(self.project_config, flow_config)
        flow.run(self.org_config)

        self.assertEqual("supername", flow.results[-1].result)
Exemplo n.º 8
0
    def test_call_task_not_found(self, mock_class):
        """ A flow with reference to a task that doesn't exist in the
        project will throw an AttributeError """

        flow_config = FlowConfig({
            'description': 'Run two tasks',
            'tasks': {
                1: {
                    'task': 'pass_name'
                },
                2: {
                    'task': 'do_delightulthings'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)

        self.assertRaises(AttributeError, flow)
Exemplo n.º 9
0
    def test_task_raises_exception_ignore(self, mock_class):
        """ A flow continues when a task configured with ignore_failure raises an exception """

        flow_config = FlowConfig({
            'description': 'Run a task',
            'tasks': {
                1: {
                    'task': 'raise_exception',
                    'ignore_failure': True
                },
                2: {
                    'task': 'pass_name'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()
        self.assertEquals(2, len(flow.tasks))
Exemplo n.º 10
0
    def test_call_one_task(self, mock_class):
        """ A flow with one task will execute the task """
        flow_config = FlowConfig({
            "description": "Run one task",
            "steps": {
                1: {
                    "task": "pass_name"
                }
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        self.assertTrue(
            any("Flow Description: Run one task" in s
                for s in self.flow_log["info"]))

        self.assertEqual([{"name": "supername"}], flow.step_return_values)
        self.assertEqual(1, len(flow.steps))
Exemplo n.º 11
0
 def test_get_summary(self):
     flow_config = FlowConfig({
         "description": "test description",
         "steps": {
             "1": {
                 "flow": "nested_flow_2"
             }
         },
     })
     flow = FlowCoordinator(self.project_config,
                            flow_config,
                            name="test_flow")
     actual_output = flow.get_summary()
     expected_output = ("Description: test description" +
                        "\n1) flow: nested_flow_2" +
                        "\n    1) task: pass_name" +
                        "\n    2) flow: nested_flow" +
                        "\n        1) task: pass_name")
     self.assertEqual(expected_output, actual_output)
Exemplo n.º 12
0
    def test_run__task_raises_exception_ignore(self):
        """ A flow continues when a task configured with ignore_failure raises an exception """

        flow_config = FlowConfig({
            "description": "Run a task",
            "steps": {
                1: {
                    "task": "raise_exception",
                    "ignore_failure": True
                },
                2: {
                    "task": "pass_name"
                },
            },
        })
        flow = FlowCoordinator(self.project_config, flow_config)
        flow.run(self.org_config)
        self.assertEqual(2, len(flow.results))
        self.assertIsNotNone(flow.results[0].exception)
Exemplo n.º 13
0
    def test_call_one_task(self, mock_class):
        """ A flow with one task will execute the task """
        flow_config = FlowConfig({
            'description': 'Run one task',
            'steps': {
                1: {
                    'task': 'pass_name'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        self.assertTrue(
            any("Flow Description: Run one task" in s
                for s in self.flow_log['info']))

        self.assertEqual([{'name': 'supername'}], flow.step_return_values)
        self.assertEqual(1, len(flow.steps))
Exemplo n.º 14
0
    def test_init__options(self):
        """ A flow can accept task options and pass them to the task. """

        # instantiate a flow with two tasks
        flow_config = FlowConfig(
            {
                "description": "Run two tasks",
                "steps": {1: {"task": "name_response", "options": {"response": "foo"}}},
            }
        )

        flow = FlowCoordinator(
            self.project_config,
            flow_config,
            options={"name_response": {"response": "bar"}},
        )

        # the first step should have the option
        self.assertEqual("bar", flow.steps[0].task_config["options"]["response"])
Exemplo n.º 15
0
    def test_flow_prints_org_id(self, mock_class):
        """ A flow with an org prints the org ID """

        flow_config = FlowConfig({
            'description': 'Run two tasks',
            'tasks': {
                1: {
                    'task': 'pass_name'
                },
                2: {
                    'task': 'pass_name'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        org_id_logs = [s for s in self.flow_log['info'] if ORG_ID in s]

        self.assertEqual(1, len(org_id_logs))
Exemplo n.º 16
0
 def test_nested_flow(self, mock_class):
     """ Flows can run inside other flows """
     flow_config = FlowConfig({
         'description': 'Run a task and a flow',
         'steps': {
             1: {
                 'task': 'pass_name'
             },
             2: {
                 'flow': 'nested_flow'
             },
         },
     })
     flow = BaseFlow(self.project_config, flow_config, self.org_config)
     flow()
     self.assertEqual(2, len(flow.steps))
     self.assertEqual(
         flow.step_return_values[0],
         flow.step_return_values[1][0],
     )
Exemplo n.º 17
0
    def test_run__option_backref_not_found(self):
        # instantiate a flow with two tasks
        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "task": "pass_name"
                },
                2: {
                    "task": "name_response",
                    "options": {
                        "response": "^^bogus.name"
                    },
                },
            },
        })

        flow = FlowCoordinator(self.project_config, flow_config)
        with self.assertRaises(NameError):
            flow.run(self.org_config)
Exemplo n.º 18
0
    def test_flow_prints_org_id_once_only(self, mock_class):
        """ A flow with sf tasks prints the org ID only once."""

        flow_config = FlowConfig({
            'description': 'Run two tasks',
            'steps': {
                1: {
                    'task': 'sfdc_task'
                },
                2: {
                    'task': 'sfdc_task'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        org_id_logs = [s for s in self.flow_log['info'] if ORG_ID in s]

        self.assertEqual(1, len(org_id_logs))
Exemplo n.º 19
0
    def test_run__prints_org_id(self):
        """ A flow with an org prints the org ID """

        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "task": "pass_name"
                },
                2: {
                    "task": "sfdc_task"
                }
            },
        })
        flow = FlowCoordinator(self.project_config, flow_config)
        flow.run(self.org_config)

        org_id_logs = [s for s in self.flow_log["info"] if ORG_ID in s]

        self.assertEqual(1, len(org_id_logs))
Exemplo n.º 20
0
    def test_flow_prints_org_id_once_only(self, mock_class):
        """ A flow with sf tasks prints the org ID only once."""

        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "task": "sfdc_task"
                },
                2: {
                    "task": "sfdc_task"
                }
            },
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        org_id_logs = [s for s in self.flow_log["info"] if ORG_ID in s]

        self.assertEqual(1, len(org_id_logs))
Exemplo n.º 21
0
    def test_run__option_backrefs(self):
        """ A flow's options reach into return values from other tasks. """

        # instantiate a flow with two tasks
        flow_config = FlowConfig(
            {
                "description": "Run two tasks",
                "steps": {
                    1: {"task": "pass_name"},
                    2: {
                        "task": "name_response",
                        "options": {"response": "^^pass_name.name"},
                    },
                },
            }
        )

        flow = FlowCoordinator(self.project_config, flow_config)
        flow.run(self.org_config)
        # the flow results for the second task should be 'name'
        self.assertEqual("supername", flow.results[1].result)
Exemplo n.º 22
0
    def test_run__skip_from_init(self):
        """ A flow can receive during init a list of tasks to skip """

        # instantiate a flow with two tasks
        flow_config = FlowConfig(
            {
                "description": "Run two tasks",
                "steps": {
                    1: {"task": "pass_name"},
                    2: {
                        "task": "name_response",
                        "options": {"response": "^^pass_name.name"},
                    },
                },
            }
        )
        flow = FlowCoordinator(self.project_config, flow_config, skip=["name_response"])
        flow.run(self.org_config)

        # the number of results should be 1 instead of 2
        self.assertEqual(1, len(flow.results))
Exemplo n.º 23
0
    def test_call_many_tasks(self, mock_class):
        """ A flow with many tasks will dispatch each task """
        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "task": "pass_name"
                },
                2: {
                    "task": "pass_name"
                }
            },
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        self.assertEqual([{
            "name": "supername"
        }, {
            "name": "supername"
        }], flow.step_return_values)
        self.assertEqual(2, len(flow.steps))
Exemplo n.º 24
0
    def test_call_many_tasks(self, mock_class):
        """ A flow with many tasks will dispatch each task """
        flow_config = FlowConfig({
            'description': 'Run two tasks',
            'steps': {
                1: {
                    'task': 'pass_name'
                },
                2: {
                    'task': 'pass_name'
                },
            }
        })
        flow = BaseFlow(self.project_config, flow_config, self.org_config)
        flow()

        self.assertEqual([{
            'name': 'supername'
        }, {
            'name': 'supername'
        }], flow.step_return_values)
        self.assertEqual(2, len(flow.steps))
Exemplo n.º 25
0
    def test_run(self):
        flow_config = FlowConfig({
            "checks": [{
                "when": "tasks.log(level='info', line='plan') or True",
                "action": "error",
                "message": "Failed plan check",
            }],
            "steps": {
                1: {
                    "task":
                    "log",
                    "options": {
                        "level": "info",
                        "line": "step"
                    },
                    "checks": [{
                        "when": "tasks.log(level='info', line='plan') or True",
                        "action": "error",
                        "message": "Failed step check",
                    }],
                }
            },
        })
        flow = PreflightFlowCoordinator(self.project_config, flow_config)
        flow.run(self.org_config)

        self.assertDictEqual(
            {
                None: [{
                    "status": "error",
                    "message": "Failed plan check"
                }],
                "log": [{
                    "status": "error",
                    "message": "Failed step check"
                }],
            },
            flow.preflight_results,
        )
Exemplo n.º 26
0
    def test_find_step_by_name__flow(self, mock_class):
        flow_config = FlowConfig({
            "description": "Run two tasks",
            "steps": {
                1: {
                    "flow": "nested_flow"
                },
                2: {
                    "task": "name_response",
                    "options": {
                        "response": "^^nested_flow.pass_name.name",
                        "from_flow": "^^nested_flow.name",
                    },
                },
            },
        })

        flow = BaseFlow(self.project_config, flow_config, self.org_config)

        flow()

        step = flow._find_step_by_name("nested_flow")
        self.assertIsInstance(step, BaseFlow)
Exemplo n.º 27
0
    def run_flow(self, project_config, org_config):
        # Add the repo root to syspath to allow for custom tasks and flows in
        # the repo
        sys.path.append(project_config.repo_root)

        flow = getattr(project_config, 'flows__{}'.format(self.flow))
        if not flow:
            raise FlowNotFoundError('Flow not found: {}'.format(self.flow))
        flow_config = FlowConfig(flow)

        callbacks = None
        if settings.METACI_FLOW_CALLBACK_ENABLED:
            from metaci.build.flows import MetaCIFlowCallback
            callbacks = MetaCIFlowCallback(buildflow_id=self.pk)

        # Create the flow and handle initialization exceptions
        self.flow_instance = FlowCoordinator(project_config,
                                             flow_config,
                                             name=self.flow,
                                             callbacks=callbacks)

        # Run the flow
        return self.flow_instance.run(org_config)
Exemplo n.º 28
0
    def test_render_task_config_empty_value(self, mock_class):
        """ The _render_task_config method skips option values of None """

        # instantiate a flow with two tasks
        flow_config = FlowConfig({
            "description": "Run a tasks",
            "steps": {
                1: {
                    "task": "name_response",
                    "options": {
                        "response": None
                    }
                }
            },
        })

        flow = BaseFlow(self.project_config, flow_config, self.org_config)

        flow()

        task = flow._find_step_by_name("name_response")
        config = flow._render_task_config(task)
        self.assertEquals(["Options:"], config)
Exemplo n.º 29
0
    def test_is_callable(self, mock_class):
        """ BaseFlow exposes itself as a callable for use """
        flow_config = FlowConfig({})
        flow = BaseFlow(self.project_config, flow_config, self.org_config)

        self.assertIsInstance(flow, Callable)
Exemplo n.º 30
0
def flow_run(config, flow_name, org, delete_org, debug, o, skip, no_prompt):
    # Check environment
    check_keychain(config)

    # Get necessary configs
    if org:
        org_config = config.project_config.get_org(org)
    else:
        org, org_config = config.project_config.keychain.get_default_org()
        if not org_config:
            raise click.UsageError(
                '`cci flow run` requires an org.'
                ' No org was specified and default org is not set.')

    org_config = check_org_expired(config, org, org_config)

    if delete_org and not org_config.scratch:
        raise click.UsageError(
            '--delete-org can only be used with a scratch org')

    flow = getattr(config.project_config, 'flows__{}'.format(flow_name))
    if not flow:
        raise FlowNotFoundError('Flow not found: {}'.format(flow_name))
    flow_config = FlowConfig(flow)
    if not flow_config.config:
        raise click.UsageError(
            'No configuration found for flow {}'.format(flow_name))

    # Get the class to look up options
    class_path = flow_config.config.get('class_path',
                                        'cumulusci.core.flows.BaseFlow')
    flow_class = import_class(class_path)

    exception = None

    # Parse command line options and add to task config
    options = {}
    if o:
        for option in o:
            options[option[0]] = option[1]

    # Create the flow and handle initialization exceptions
    try:
        flow = flow_class(config.project_config,
                          flow_config,
                          org_config,
                          options,
                          skip,
                          name=flow_name)
    except TaskRequiresSalesforceOrg as e:
        exception = click.UsageError(
            'This flow requires a salesforce org.  Use org default <name> to set a default org or pass the org name with the --org option'
        )
    except TaskOptionsError as e:
        exception = click.UsageError(e.message)
        handle_exception_debug(config, debug, e, throw_exception=exception)
    except Exception as e:
        handle_exception_debug(config, debug, e, no_prompt=no_prompt)

    if not exception:
        # Run the flow and handle exceptions
        try:
            flow()
        except TaskOptionsError as e:
            exception = click.UsageError(e.message)
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except ApexTestException as e:
            exception = click.ClickException('Failed: ApexTestException')
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except BrowserTestFailure as e:
            exception = click.ClickException('Failed: BrowserTestFailure')
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except MetadataComponentFailure as e:
            exception = click.ClickException(
                'Failed: MetadataComponentFailure')
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except MetadataApiError as e:
            exception = click.ClickException('Failed: MetadataApiError')
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except ScratchOrgException as e:
            exception = click.ClickException('ScratchOrgException: {}'.format(
                e.message))
            handle_exception_debug(config, debug, e, throw_exception=exception)
        except Exception as e:
            handle_exception_debug(config, debug, e, no_prompt=no_prompt)

    # Delete the scratch org if --delete-org was set
    if delete_org:
        try:
            org_config.delete_org()
        except Exception as e:
            click.echo(
                'Scratch org deletion failed.  Ignoring the error below to complete the flow:'
            )
            click.echo(e.message)

    if exception:
        handle_sentry_event(config, no_prompt)
        raise exception