예제 #1
0
def advance_workflow(current_step):
    """Advance the workflow for all applications able to be advanced."""
    Logger.info(
        {"current_step": current_step},
        "[/api/workflow/progress/<current_step>] Workflow advance started",
    )

    try:
        # Creating a copy of the working configuration directory
        config_dir = config_lib.create_temporary_config_copy()
        config_lib.change_environment(
            Configuration.get_config_default_branch(), config_dir)
        project_config = config_lib.get_project_config(config_dir)

        report_status, report = workflow_lib.advance_workflow(
            config_dir, project_config, current_step)

        status, message = _get_status_and_message(report_status)

        # Clean up
        non_blocking_clean(
            config_dir,
            message_prefix="[/api/workflow/progress/<current_step>]")

        # HTTP Response
        Logger.info(
            {
                "current_step": current_step,
                "report": report
            },
            f"[/api/workflow/progress/<current_step>] {message}",
        )
        return (
            {
                "current_step": current_step,
                "report": report,
                "message": message
            },
            status,
        )
    # pylint: disable=broad-except
    except Exception as err:
        Logger.error(
            {
                "current_step": current_step,
                "err": str(err)
            },
            "[/api/workflow/progress/<current_step>] Workflow advance failed",
        )

        # HTTP Response
        return (
            {
                "current_step": current_step,
                "err": str(err),
                "message": "Workflow advance failed",
            },
            HTTPStatus.INTERNAL_SERVER_ERROR,
        )
    def test_non_blocking_clean_with_error_and_message_prefix(
            self, io_mock, logger_mock):
        """Should append message prefix to log."""
        io_mock.remove.side_effect = Exception("some error during removal")

        non_blocking_clean("some/path", message_prefix="[prefix]")

        logger_mock.warn.assert_called_with(
            ANY, "[prefix] Error trying to clean temporary directory / file")
 def test_non_blocking_clean_with_error(self, io_mock, _logger_mock):
     """Should not raise an error."""
     io_mock.remove.side_effect = Exception("some error during removal")
     try:
         non_blocking_clean("some/path")
     # pylint: disable=broad-except
     except Exception:
         self.fail(
             "_non_blocking_clean should not raise an error when cleaning fails"
         )
예제 #4
0
def advance_workflow(
    config_dir: str, project_config: Dict, current_step: str
) -> Tuple[WorkflowAdvanceStatus, List[AdvanceWorkflowAppReport]]:
    """Advance the application workflow to the next step"""
    progress_report: List[AdvanceWorkflowAppReport] = []
    next_step = get_next_step(project_config, current_step)
    if next_step is None:
        raise WorkflowError("Workflow is already in final step.")
    status = WorkflowAdvanceStatus.SUCCESS

    # List applications
    try:
        apps = config.list_apps_config(config_dir)
    except Exception as err:
        raise AppListingError(err)

    for (app_name, app_config) in apps.items():
        should_app_progress = False
        tag = None
        app_dir = None
        try:
            # Determine if app is ready to progress or not
            app_dir = git.create_working_repository(
                app_name, app_config["git"]["origin"])
            should_app_progress, tag = get_app_progress_report(
                app_dir, current_step, next_step)

            # If app is ready to progress, make it advance to the next step in the workflow
            Logger.info(
                {
                    "app": app_name,
                    "tag": tag,
                    "current_step": current_step,
                    "next_step": next_step,
                },
                "Advancing to the next workflow step" if should_app_progress
                else "App is already up-to-date. Skipping.",
            )
            if should_app_progress:
                git.branch(app_dir, next_step)
                git.rebase(app_dir, current_step, onto=tag)
                git.push(app_dir)

                processes = config.get_processes(app_config)
                cron_jobs = config.get_cronjobs(app_config)

                progress_report.append({
                    "name": app_name,
                    "tag": tag,
                    "step": next_step,
                    "processes": processes,
                    "cron_jobs": cron_jobs,
                })

        # pylint: disable=broad-except
        except Exception as err:
            Logger.error(
                {
                    "app_name": app_name,
                    "config_dir": config_dir,
                    "should_app_progress": should_app_progress,
                    "tag": tag,
                    "current_step": current_step,
                    "err": str(err),
                },
                "Error while advancing the workflow",
            )
            status = WorkflowAdvanceStatus.FAIL

        if app_dir is not None:
            non_blocking_clean(app_dir)

    return status, progress_report
예제 #5
0
def init_workflow(
        organization: str, app_name: str, git_provider: AbstractGitProvider
) -> Tuple[WorkflowInitStatus, Report]:
    """Initialize the workflow of an application's repository by creating
    all workflow branches. This function is idempotent which means it will not
    try to recreate a branch that already exists. However if a branch
    already exists but is not protected, it will be set to protected."""

    # Create temporary copy for avoiding concurrency problems
    config_path = config.create_temporary_config_copy()

    # Switch to staging environment in order to get application configuration
    config.change_environment("staging", config_path)

    # Get application configuration to get the list of workflow branches
    app_config = config.get_app_config(app_name, config_path)
    master_tag = GitConfiguration.get_master_tag()

    workflow_branches = _get_workflow_branches(app_config, master_tag)
    branches = {}
    status = WorkflowInitStatus.SUCCESS

    if len(workflow_branches) != 0:
        status = WorkflowInitStatus.FAIL
        try:
            # Get user_login linked to the GITHUB_TOKEN
            user_info = git_provider.get_user_info()
            user_login = user_info.login if user_info else None
            Logger.info({"user_login": user_login}, "User login retrieved")

            # Get the last commit's sha on master branch
            branch = git_provider.get_branch(organization, app_name,
                                             master_tag)
            master_head_sha = branch and branch.commit and branch.commit.sha
            Logger.info({"sha": master_head_sha},
                        "master last commit sha retrieved")

            # Sync all workflow branches with master's head and
            # protect them by limiting push rights to user_login
            for branch_name in workflow_branches:
                branches[branch_name] = _create_and_protect_branch(
                    organization, app_name, branch_name, master_head_sha,
                    user_login, git_provider)
        except GitResourceNotFoundError as err:
            Logger.error(
                {
                    "master_branch_name": master_tag,
                    "err": err
                },
                "master last commit sha failed to be retrieved.",
            )
        except GitProviderError as err:
            Logger.error(
                {
                    "organization": organization,
                    "app_name": app_name,
                    "err": err
                },
                "Fail to initialize workflow",
            )
        else:
            status = WorkflowInitStatus.SUCCESS

    # Clean temporary copy
    non_blocking_clean(config_path)

    return status, branches
 def test_non_blocking_clean(self, io_mock, _logger_mock):
     """Should clean the folder/file provided."""
     non_blocking_clean("some/path")
     io_mock.remove.assert_called_with("some/path")