Exemplo n.º 1
0
 def _check_old_yaml_format(self):
     if self.flow_config.steps is None:
         if "tasks" in self.flow_config.config:
             raise FlowConfigError(
                 'Old flow syntax detected.  Please change from "tasks" to "steps" in the flow definition.'
             )
         else:
             raise FlowConfigError("No steps found in the flow definition")
Exemplo n.º 2
0
 def _get_steps_ordered(self):
     if self.flow_config.steps is None:
         if self.flow_config.tasks:
             raise FlowConfigError(
                 'Old flow syntax detected.  Please change from "tasks" to "steps" in the flow definition'
             )
         else:
             raise FlowConfigError("No steps found in the flow definition")
     if not self.nested:
         self._check_infinite_flows(self.flow_config.steps)
     steps = []
     for step_num, config in list(self.flow_config.steps.items()):
         if "flow" in config and "task" in config:
             raise FlowConfigError(
                 '"flow" and "task" in same config item: {}'.format(config))
         if ("flow" in config and config["flow"] == "None") or (
                 "task" in config and config["task"] == "None"):
             # allows skipping flows/tasks using YAML overrides
             continue
         parsed_step_num = LooseVersion(str(step_num))
         if "flow" in config:  # nested flow
             flow_config = self.project_config.get_flow(config["flow"])
             steps.append((
                 parsed_step_num,
                 {
                     "step_config": config,
                     "flow_config": flow_config
                 },
             ))
         elif "task" in config:
             task_config = self.project_config.get_task(config["task"])
             steps.append((
                 parsed_step_num,
                 {
                     "step_config": config,
                     "task_config": task_config
                 },
             ))
     steps.sort()  # sort by step number
     return steps
Exemplo n.º 3
0
    def _visit_step(
        self,
        number,
        step_config,
        project_config,
        visited_steps=None,
        parent_options=None,
        parent_ui_options=None,
        from_flow=None,
    ):
        """
        for each step (as defined in the flow YAML), _visit_step is called with only
        the first two parameters. this takes care of validating the step, collating the
        option overrides, and if it is a task, creating a StepSpec for it.

        If it is a flow, we recursively call _visit_step with the rest of the parameters of context.

        :param number: StepVersion representation of the current step number
        :param step_config: the current step's config (dict from YAML)
        :param visited_steps: used when called recursively for nested steps, becomes the return value
        :param parent_options: used when called recursively for nested steps, options from parent flow
        :param parent_ui_options: used when called recursively for nested steps, UI options from parent flow
        :param from_flow: used when called recursively for nested steps, name of parent flow
        :return: List[StepSpec] a list of all resolved steps including/under the one passed in
        """
        number = StepVersion(str(number))

        if visited_steps is None:
            visited_steps = []
        if parent_options is None:
            parent_options = {}
        if parent_ui_options is None:
            parent_ui_options = {}

        # Step Validation
        # - A step is either a task OR a flow.
        if all(k in step_config for k in ("flow", "task")):
            raise FlowConfigError(
                f"Step {number} is configured as both a flow AND a task. \n\t{step_config}."
            )

        # Skips
        # - either in YAML (with the None string)
        # - or by providing a skip list to the FlowRunner at initialization.
        if (("flow" in step_config and step_config["flow"] == "None")
                or ("task" in step_config and step_config["task"] == "None") or
            ("task" in step_config and step_config["task"] in self.skip)):
            visited_steps.append(
                StepSpec(
                    step_num=number,
                    task_name=step_config.get("task", step_config.get("flow")),
                    task_config=step_config.get("options", {}),
                    task_class=None,
                    project_config=project_config,
                    from_flow=from_flow,
                    skip=
                    True,  # someday we could use different vals for why skipped
                ))
            return visited_steps

        if "task" in step_config:
            name = step_config["task"]

            # get the base task_config from the project config, as a dict for easier manipulation.
            # will raise if the task doesn't exist / is invalid
            task_config = project_config.get_task(name)
            task_config_dict = copy.deepcopy(task_config.config)
            if "options" not in task_config_dict:
                task_config_dict["options"] = {}

            # merge the options together, from task_config all the way down through parent_options
            step_overrides = copy.deepcopy(parent_options.get(name, {}))
            step_overrides.update(step_config.get("options", {}))
            task_config_dict["options"].update(step_overrides)

            # merge UI options from task config and parent flow
            if "ui_options" not in task_config_dict:
                task_config_dict["ui_options"] = {}
            step_ui_overrides = copy.deepcopy(parent_ui_options.get(name, {}))
            step_ui_overrides.update(step_config.get("ui_options", {}))
            task_config_dict["ui_options"].update(step_ui_overrides)

            # merge checks from task config and flow step
            if "checks" not in task_config_dict:
                task_config_dict["checks"] = []
            task_config_dict["checks"].extend(step_config.get("checks", []))

            # merge runtime options
            if name in self.runtime_options:
                task_config_dict["options"].update(self.runtime_options[name])

            # get implementation class. raise/fail if it doesn't exist, because why continue
            try:
                task_class = import_global(task_config_dict["class_path"])
            except (ImportError, AttributeError):
                raise FlowConfigError(f"Task named {name} has bad classpath")

            visited_steps.append(
                StepSpec(
                    step_num=number,
                    task_name=name,
                    task_config=task_config_dict,
                    task_class=task_class,
                    project_config=task_config.project_config,
                    allow_failure=step_config.get("ignore_failure", False),
                    from_flow=from_flow,
                    when=step_config.get("when"),
                ))
            return visited_steps

        if "flow" in step_config:
            name = step_config["flow"]
            if from_flow:
                path = ".".join([from_flow, name])
            else:
                path = name
            step_options = step_config.get("options", {})
            step_ui_options = step_config.get("ui_options", {})
            flow_config = project_config.get_flow(name)
            for sub_number, sub_stepconf in flow_config.steps.items():
                # append the flow number to the child number, since its a LooseVersion.
                # e.g. if we're in step 2.3 which references a flow with steps 1-5, it
                #   simply ends up as five steps: 2.3.1, 2.3.2, 2.3.3, 2.3.4, 2.3.5
                # TODO: how does this work with nested flowveride? what does defining step 2.3.2 later do?
                num = f"{number}/{sub_number}"
                self._visit_step(
                    number=num,
                    step_config=sub_stepconf,
                    project_config=flow_config.project_config,
                    visited_steps=visited_steps,
                    parent_options=step_options,
                    parent_ui_options=step_ui_options,
                    from_flow=path,
                )
        return visited_steps