def get(self, id):
     raise GitlabAuthenticationError('Unauthorized', 401)
예제 #2
0
def update_project(project: Project,
                   local_file_path: str,
                   gitlab_file_path: str,
                   name: str,
                   chart_name: str,
                   chart_ref: str,
                   old_version: str,
                   new_version: str,
                   remove_source_branch: bool = False,
                   squash: bool = False,
                   automerge: bool = False,
                   merge_major: bool = False,
                   assignee_ids: List[int] = [],
                   labels: List[str] = []) -> ProjectMergeRequest:
    """Main function for handling branches, merge requests and version in file.

    - create/update a branch
    - create/update a merge request
    - replace the version in a file and updates the content to a Gitlab repo

    Args:
        project (gitlab.v4.objects.Project): Gitlab project object
        local_file_path (str): path to the local file
        gitlab_file_path (str): path to file on Gitlab
        name (str): name of chart
        chart_name (str): name of chart repository
        chart_ref (str): reference of chart
        old_version (str): current version of chart
        new_version (str): new version of chart
        remove_source_branch (bool, optional):. remove brunch after merge. Defaults to 'False'.
        squash (bool, optional):. squash commits after merge. Defaults to 'False'.
        automerge (bool, optional):. merge request automatically
        merge_major (bool, optional):. merge also major updates
        assignee_ids (List[int], optional): list of assignee id's to assign mr. Defaults to [].
        labels (List[str], optional): list of labels to set. Defaults to [].

    Raises:
        TypeError: parameter 'project' is not of type 'gitlab.v4.objects.Project'
        LookupError: branch could not be created
        GitlabUpdateError: unable to update merge request
        GitlabCreateError: unable to create branch
        GitlabCreateError: unable to create merge request
        GitlabUploadError: unable to upload new file content

    Returns:
        gitlab.v4.objects.ProjectMergeRequest: Gitlab merge request object
    """
    if not isinstance(project, gitlab.v4.objects.Project):
        raise TypeError(
            f"parameter 'project' must be of type 'gitlab.v4.objects.Project', got '{type(project)}'"
        )

    mergerequest_title = templates.merge_request_title.format(
        CHART_NAME=chart_name, NEW_VERSION=new_version)

    try:
        merge_request = eval_merge_requests(project=project,
                                            title=mergerequest_title,
                                            chart_name=chart_name)
    except Exception as e:
        raise LookupError(f"unable check existing merge requests. {str(e)}")

    if merge_request.closed:
        return

    if merge_request.exists:
        pass  # go on, maybe a file update is needed

    is_major = (semver.VersionInfo.parse(new_version.lstrip("v")).major >
                semver.VersionInfo.parse(old_version.lstrip("v")).major)

    if not automerge:
        config = "🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\n\n"

    if automerge and is_major and merge_major:
        config = "🚦 **Automerge**: Enabled by config. Merge request will merge automatically.\n\n"

    if automerge and is_major and not merge_major:
        config = (
            "🚦 **Automerge**: Enabled by config, but disabled for major updates. "
            "Please merge this manually once you are satisfied.\n\n")

    config += "🔕 **Ignore**: Close this MR and you won't be reminded about this update again."

    description = templates.description.format(NAME=name,
                                               CHART_REF=chart_ref,
                                               OLD_VERSION=old_version,
                                               NEW_VERSION=new_version,
                                               CONFIG=config)
    branch_name = templates.branch_name.format(CHART_NAME=chart_name)

    mr = None
    if merge_request.update:
        try:
            mr = get_merge_request_by_title(
                project=project,
                title=pattern.mr_title.format(CHART_NAME=chart_name),
                state="opened",
                sort="desc")
            if not mr:
                raise LookupError(f"merge request '{chart_name}' not found!")

            mr = mr[0]  # get newest merge request
            if labels:
                mr.labels = labels
            mr.title = mergerequest_title
            mr.description = description
            if remove_source_branch is not None:
                mr.remove_source_branch = str(
                    remove_source_branch).lower() == "true"
            if squash is not None:
                mr.squash = str(squash).lower() == "true"
            mr.save()
        except Exception as e:
            raise GitlabUpdateError(f"cannot update merge request. {str(e)}")

    if merge_request.missing:
        try:
            create_branch(project=project, branch_name=branch_name)
        except GitlabCreateError as e:
            logging.debug(
                f"cannot create branch '{branch_name}'. {str(e.error_message)}"
            )
        except Exception as e:
            raise GitlabCreateError(
                f"cannot create branch '{branch_name}'. {str(e)}")

        try:
            mr = create_merge_request(
                project=project,
                branch_name=branch_name,
                description=description,
                title=mergerequest_title,
                remove_source_branch=remove_source_branch,
                squash=squash,
                assignee_ids=assignee_ids,
                labels=labels)
        except Exception as e:
            raise GitlabCreateError(
                f"unable to create merge request. {str(e)}")

    try:
        old_chart_version = re.compile(
            pattern=templates.chart_version.format(VERSION=old_version),
            flags=re.IGNORECASE)
        new_chart_version = templates.chart_version.format(VERSION=new_version)
        with open(file=local_file_path, mode="r+") as f:
            old_content = f.read()
            new_content = re.sub(pattern=old_chart_version,
                                 repl=new_chart_version,
                                 string=old_content)

            update_file(project=project,
                        branch_name=branch_name,
                        commit_msg=mergerequest_title,
                        content=new_content,
                        path_to_file=gitlab_file_path)
    except Exception as e:
        raise GitlabUploadError(f"unable to upload file. {str(e)}")

    try:
        if automerge:
            mr.merge(merge_when_pipeline_succeeds=True)
    except GitlabAuthenticationError as e:
        raise GitlabAuthenticationError(
            "Authentication not set correctly. 'Helminator' User must have the role 'Maintainer'"
        )
    except Exception as e:
        raise Exception(f"cannot merge MR. {e}")

    return mr
 def delete(self, hook_id):
     raise GitlabAuthenticationError('Unauthorized', 401)
 def get(self, name, lazy):
     raise GitlabAuthenticationError('Unauthorized', 401)
 def create(self, data):
     raise GitlabAuthenticationError('Unauthorized', 401)
예제 #6
0
    async def http_request(self,
                           verb,
                           path,
                           query_data=None,
                           post_data=None,
                           streamed=False,
                           files=None,
                           **kwargs):
        query_data = query_data or {}
        url = self._build_url(path)

        params = {}
        utils.copy_dict(params, query_data)

        # Deal with kwargs: by default a user uses kwargs to send data to the
        # gitlab server, but this generates problems (python keyword conflicts
        # and python-gitlab/gitlab conflicts).
        # So we provide a `query_parameters` key: if it's there we use its dict
        # value as arguments for the gitlab server, and ignore the other
        # arguments, except pagination ones (per_page and page)
        if "query_parameters" in kwargs:
            utils.copy_dict(params, kwargs["query_parameters"])
            for arg in ("per_page", "page"):
                if arg in kwargs:
                    params[arg] = kwargs[arg]
        else:
            utils.copy_dict(params, kwargs)

        opts = {"headers": self._create_headers("application/json")}

        # If timeout was passed into kwargs, allow it to override the default
        timeout = kwargs.get("timeout")

        # We need to deal with json vs. data when uploading files
        if files:
            data = post_data
            json = None
            del opts["headers"]["Content-type"]
        else:
            json = post_data
            data = None

        req = httpx.Request(verb,
                            url,
                            json=json,
                            data=data,
                            params=params,
                            files=files,
                            **opts)

        # obey the rate limit by default
        obey_rate_limit = kwargs.get("obey_rate_limit", True)
        # do not retry transient errors by default
        retry_transient_errors = kwargs.get("retry_transient_errors", False)

        # set max_retries to 10 by default, disable by setting it to -1
        max_retries = kwargs.get("max_retries", 10)
        cur_retries = 0

        while True:
            result = await self.client.send(req,
                                            stream=streamed,
                                            timeout=timeout)

            self._check_redirects(result)

            if 200 <= result.status_code < 300:
                return result

            if (429 == result.status_code and obey_rate_limit) or (
                    result.status_code in [500, 502, 503, 504]
                    and retry_transient_errors):
                if max_retries == -1 or cur_retries < max_retries:
                    wait_time = 2**cur_retries * 0.1
                    if "Retry-After" in result.headers:
                        wait_time = int(result.headers["Retry-After"])
                    cur_retries += 1
                    await asyncio.sleep(wait_time)
                    continue

            error_message = result.content
            try:
                error_json = result.json()
                for k in ("message", "error"):
                    if k in error_json:
                        error_message = error_json[k]
            except (KeyError, ValueError, TypeError):
                pass

            if result.status_code == 401:
                raise GitlabAuthenticationError(
                    response_code=result.status_code,
                    error_message=error_message,
                    response_body=result.content,
                )

            raise GitlabHttpError(
                response_code=result.status_code,
                error_message=error_message,
                response_body=result.content,
            )
예제 #7
0
    async def http_request(
        self,
        verb,
        path,
        query_data=None,
        post_data=None,
        streamed=False,
        files=None,
        **kwargs
    ):
        """Make an HTTP request to the Gitlab server.

        Args:
            verb (str): The HTTP method to call ('get', 'post', 'put',
                        'delete')
            path (str): Path or full URL to query ('/projects' or
                        'http://whatever/v4/api/projecs')
            query_data (dict): Data to send as query parameters
            post_data (dict): Data to send in the body (will be converted to
                              json)
            streamed (bool): Whether the data should be streamed
            files (dict): The files to send to the server
            **kwargs: Extra options to send to the server (e.g. sudo)

        Returns:
            A requests result object.

        Raises:
            GitlabHttpError: When the return code is not 2xx
        """
        query_data = query_data or {}
        url = self._build_url(path)

        params = {}
        utils.copy_dict(params, query_data)

        # Deal with kwargs: by default a user uses kwargs to send data to the
        # gitlab server, but this generates problems (python keyword conflicts
        # and python-gitlab/gitlab conflicts).
        # So we provide a `query_parameters` key: if it's there we use its dict
        # value as arguments for the gitlab server, and ignore the other
        # arguments, except pagination ones (per_page and page)
        if "query_parameters" in kwargs:
            utils.copy_dict(params, kwargs["query_parameters"])
            for arg in ("per_page", "page"):
                if arg in kwargs:
                    params[arg] = kwargs[arg]
        else:
            utils.copy_dict(params, kwargs)

        opts = self._get_session_opts(content_type="application/json")

        verify = opts.pop("verify")
        timeout = opts.pop("timeout")
        # If timeout was passed into kwargs, allow it to override the default
        timeout = kwargs.get("timeout", timeout)

        # We need to deal with json vs. data when uploading files
        if files:
            data = post_data
            json = None
            del opts["headers"]["Content-type"]
        else:
            json = post_data
            data = None

        # Requests assumes that `.` should not be encoded as %2E and will make
        # changes to urls using this encoding. Using a prepped request we can
        # get the desired behavior.
        # The Requests behavior is right but it seems that web servers don't
        # always agree with this decision (this is the case with a default
        # gitlab installation)
        req = httpx.Request(
            verb, url, json=json, data=data, params=params, files=files, **opts
        )
        prepped = self.client.prepare_request(req)
        prepped.url = utils.sanitized_url(prepped.url)
        settings = self.client.merge_environment_settings(
            prepped.url, {}, streamed, verify, None
        )

        # obey the rate limit by default
        obey_rate_limit = kwargs.get("obey_rate_limit", True)
        # do not retry transient errors by default
        retry_transient_errors = kwargs.get("retry_transient_errors", False)

        # set max_retries to 10 by default, disable by setting it to -1
        max_retries = kwargs.get("max_retries", 10)
        cur_retries = 0

        while True:
            result = await self.client.send(prepped, timeout=timeout, **settings)

            self._check_redirects(result)

            if 200 <= result.status_code < 300:
                return result

            if (429 == result.status_code and obey_rate_limit) or (
                result.status_code in [500, 502, 503, 504] and retry_transient_errors
            ):
                if max_retries == -1 or cur_retries < max_retries:
                    wait_time = 2 ** cur_retries * 0.1
                    if "Retry-After" in result.headers:
                        wait_time = int(result.headers["Retry-After"])
                    cur_retries += 1
                    time.sleep(wait_time)
                    continue

            error_message = result.content
            try:
                error_json = result.json()
                for k in ("message", "error"):
                    if k in error_json:
                        error_message = error_json[k]
            except (KeyError, ValueError, TypeError):
                pass

            if result.status_code == 401:
                raise GitlabAuthenticationError(
                    response_code=result.status_code,
                    error_message=error_message,
                    response_body=result.content,
                )

            raise GitlabHttpError(
                response_code=result.status_code,
                error_message=error_message,
                response_body=result.content,
            )