Ejemplo n.º 1
0
    def post_request(self, **values_to_unpack) -> requests:
        """Performs a HTTP POST request to the Bamboo server.

        :param values_to_unpack: Values to un-pack in order to construct the HTTP POST request
        :return: A requests response object
        :raise: Custom exception on HTTP communication errors
        """

        url = values_to_unpack.get('url', "")
        headers = values_to_unpack.get('header', "") or self.http_header
        data = values_to_unpack.get('data', {})
        timeout = values_to_unpack.get('timeout', 30)
        allow_redirects = values_to_unpack.get('allow_redirects', False)

        try:
            response = HTTP.post(url=url,
                                 auth=self.auth,
                                 headers=headers,
                                 data=data,
                                 timeout=timeout,
                                 allow_redirects=allow_redirects)
        except (requests.ConnectionError, requests.ConnectTimeout,
                requests.HTTPError, requests.RequestException,
                requests.Timeout) as exception:
            error_message = f"Error when requesting URL: '{url}'{LINE_SEP}{exception}"
            LOGGER.error(error_message)
            exception = HTTPErrorException(error_message=error_message)
            raise exception
        except Exception as exception:
            error_message = f"Unknown error when requesting URL: '{url}'{LINE_SEP}{exception}"
            LOGGER.error(error_message)
            exception = HTTPErrorException(error_message=error_message)
            raise exception

        return response
Ejemplo n.º 2
0
    def __load_credentials() -> tuple:
        if not all([BAMBOO_USER and BAMBOO_PASS]):
            LOGGER.warning(
                "No credentials found while initializing the module! Please ensure you set them in explicitly!"
            )
            return None, None

        return BAMBOO_USER, BAMBOO_PASS
Ejemplo n.º 3
0
    def stop_build(self,
                   server_url: str = None,
                   plan_build_key: str = None) -> dict:
        """Stop a running plan build from Bamboo using Bamboo API.
        This method is not officially supported, as so please be aware there might be conflicts when upgrading to a new
        Bamboo version. What I have observed between Bamboo official release, is that the return code for the POST URL
        returns different HTTP codes (like 200, 302). It all depends on the implementation in the Bamboo server.

        :param server_url: Bamboo server URL used in API call [str]
        Optional. Use this if you have a cluster of Bamboo servers and need to swap between servers.
        :param plan_build_key: Bamboo plan build key [str]
        :return: A dictionary containing HTTP status_code and request content
        """

        server_url = server_url or self.server_url

        url = self.stop_plan_url_mask.format(server_url=server_url)
        url = f"{url}?planResultKey={plan_build_key}"

        if self.verbose:
            LOGGER.debug(f"URL used to stop plan: '{url}'")

        # Stop a build by performing a HTTP POST request and check HTTP response code
        http_post_response = self.post_request(url=url)
        if http_post_response.status_code not in [200, 302]:
            return self.pack_response_to_client(
                response=False,
                status_code=http_post_response.status_code,
                content=http_post_response.text,
                url=url)

        # Send response to client
        return self.pack_response_to_client(
            response=True,
            status_code=http_post_response.status_code,
            content=http_post_response,
            url=url)
Ejemplo n.º 4
0
    def query_plan(self, server_url: str = None, plan_key: str = None) -> dict:
        """Query a plan build using Bamboo API.

        :param server_url: Bamboo server URL used in API call [str]
        Optional. Use this if you have a cluster of Bamboo servers and need to swap between servers.
        :param plan_key: Bamboo plan key [str]
        :return: A dictionary containing HTTP status_code and request content
        :raise: Custom exception on JSON encoding error
        """

        server_url = server_url or self.server_url
        plan_key = plan_key or self.plan_key

        url = self.plan_results_url_mask.format(server_url=server_url)
        url = f"{url}{plan_key}.json?max-results=10000"

        if self.verbose:
            LOGGER.debug(f"URL used in query: '{url}'")

        # Query a build by performing a HTTP GET request and check HTTP response code
        http_get_response = self.get_request(url=url)
        if http_get_response.status_code != 200:
            return self.pack_response_to_client(
                response=False,
                status_code=http_get_response.status_code,
                content=http_get_response.text,
                url=url)

        try:
            # Get the JSON reply from the web page
            http_get_response.encoding = "utf-8"
            response_json = http_get_response.json()
        except ValueError as exception:
            error_message = f"Error encoding to JSON: {exception}"
            LOGGER.error(error_message)
            exception = EncodingJSONException(error_message=error_message)
            raise exception
        except Exception as exception:
            error_message = f"Unknown error when trying to return json-encoded content: {exception}"
            LOGGER.error(error_message)
            exception = EncodingJSONException(error_message=error_message)
            raise exception

        # Send response to client
        return self.pack_response_to_client(
            response=True,
            status_code=http_get_response.status_code,
            content=response_json,
            url=url)
Ejemplo n.º 5
0
    def get_artifact(self,
                     url: str = None,
                     destination_file: str = None) -> dict:
        """Download artifacts from Bamboo plan build run.

        :param url: URL used in to download the artifact [str]
        :param destination_file: Full path to destination file [str]
        :return: A dictionary containing HTTP status_code and request content
        :raise: Custom exception on download error
        """

        if not url or not destination_file:
            return {'content': "Incorrect input provided!"}

        if self.verbose:
            LOGGER.debug(f"URL used to download artifact: '{url}'")

        # Query a build by performing a HTTP GET request and check HTTP response code
        http_get_response = self.get_request(url=url)
        if http_get_response.status_code != 200:
            return self.pack_response_to_client(
                response=False,
                status_code=http_get_response.status_code,
                content=http_get_response.text,
                url=url)

        try:
            get_file = requests.get(url)

            with open(destination_file, 'wb') as f:
                f.write(get_file.content)
        except ValueError as exception:
            error_message = f"Error when downloading artifact: {exception}"
            LOGGER.error(error_message)
            exception = DownloadErrorException(error_message=error_message)
            raise exception
        except Exception as exception:
            error_message = f"Unknown error when downloading artifact: {exception}"
            LOGGER.error(error_message)
            exception = DownloadErrorException(error_message=error_message)
            raise exception

        # Send response to client
        return self.pack_response_to_client(
            response=True,
            status_code=http_get_response.status_code,
            content=None,
            url=url)
Ejemplo n.º 6
0
    def query_job_for_artifacts(self,
                                server_url: str = None,
                                plan_build_key: str = None,
                                job_name: str = None,
                                artifact_names: tuple = None) -> dict:
        """Query Bamboo plan run build for stage artifacts.
        TODO: add support to get artifacts from sub-dirs as well

        :param server_url: Bamboo server URL used in API call [str]
        Optional. Use this if you have a cluster of Bamboo servers and need to swap between servers.
        :param plan_build_key: Bamboo plan build key [str]
        :param job_name: Bamboo plan job name [str]
        :param artifact_names: Names of the artifacts as in Bamboo plan stage job [tuple]
        :return: A dictionary containing HTTP status_code, request content and list of artifacts
        :raise: Custom exception on download error
        """

        server_url = server_url or self.server_url

        # Artifacts to return
        artifacts = dict()

        http_failed_conn_counter = 0
        for artifact_name in artifact_names:
            url = self.artifact_url_mask.format(server_url=server_url,
                                                plan_build_key=plan_build_key,
                                                job_name=job_name,
                                                artifact_name=artifact_name)

            if self.verbose:
                LOGGER.debug(f"URL used to query for artifacts: '{url}'")

            # Query a build by performing a HTTP GET request and check HTTP response code
            http_get_response = self.get_request(url=url)
            if http_get_response.status_code != 200:
                http_failed_conn_counter += 1
                continue

            try:
                # page = requests.get(url).text  <-- Works if Bamboo plan does not require AUTH
                soup = BeautifulSoup(http_get_response.text, 'html.parser')
                # All "<a href></a>" elements
                a_href_elements = (soup.find_all('a', href=True))

                for a_href_element in a_href_elements:
                    file_path = a_href_element['href']
                    file_name = a_href_element.extract().get_text()

                    # Do not add HREF value in case PAGE NOT FOUND error
                    if file_name != "Site homepage":
                        artifacts[file_name] = f"{server_url}{file_path}"
            except ValueError as exception:
                error_message = f"Error when downloading artifact: {exception}"
                LOGGER.error(error_message)
                exception = DownloadErrorException(error_message=error_message)
                raise exception
            except Exception as exception:
                error_message = f"Unknown error when downloading artifact: {exception}"
                LOGGER.error(error_message)
                exception = DownloadErrorException(error_message=error_message)
                raise exception

        http_return_code = 200
        if http_failed_conn_counter == len(artifact_names):
            http_return_code = 444

        response_to_client = self.pack_response_to_client(
            response=True,
            status_code=http_return_code,
            content=None,
            url=None)
        response_to_client['artifacts'] = artifacts

        # Send response to client
        return response_to_client
Ejemplo n.º 7
0
    def trigger_plan_build(self,
                           server_url: str = None,
                           plan_key: str = None,
                           req_values: tuple = None) -> dict:
        """Trigger a plan build using Bamboo API.
        TODO: deal with errors from plan restriction, like strict no of plans to build

        :param server_url: Bamboo server URL used in API call [str]
        Optional. Use this if you have a cluster of Bamboo servers and need to swap between servers.
        :param plan_key: Bamboo plan key [str]
        :param req_values: Values to insert into request (tuple)
        :return: A dictionary containing HTTP status_code and request content
        :raise: Custom exception on JSON encoding error
        """

        server_url = server_url or self.server_url
        plan_key = plan_key or self.plan_key

        # Execute all stages by default if no options received
        request_payload = {'stage&executeAllStages': [True]}
        if req_values:
            # req_values[0] = True/False
            request_payload['stage&executeAllStages'] = [req_values[0]]

            # Example
            #     req_value[1] = {'bamboo.driver': "xyz", 'bamboo.test': "xyz_1"}
            #     API supports a list as values
            for key, value in req_values[1].items():
                # Use custom revision when triggering build
                if key.lower() == 'custom.revision':
                    request_payload["bamboo.customRevision"] = [value]
                    continue

                request_payload["bamboo.{key}".format(key=key)] = [value]

        url = self.trigger_plan_url_mask.format(server_url=server_url)
        url = f"{url}{plan_key}.json"
        if self.verbose:
            LOGGER.debug(f"URL used to trigger build: '{url}'")

        # Trigger the build by performing a HTTP POST request and check HTTP response code
        http_post_response = self.post_request(
            url=url, data=json.dumps(request_payload))
        if http_post_response.status_code != 200:
            return self.pack_response_to_client(
                response=False,
                status_code=http_post_response.status_code,
                content=http_post_response.text,
                url=url)

        try:
            # Get the JSON reply from the web page
            http_post_response.encoding = "utf-8"
            response_json = http_post_response.json()
        except ValueError as exception:
            error_message = f"Error encoding to JSON: {exception}"
            LOGGER.error(error_message)
            exception = EncodingJSONException(error_message=error_message)
            raise exception
        except Exception as exception:
            error_message = f"Unknown error when trying to return json-encoded content: {exception}"
            LOGGER.error(error_message)
            exception = EncodingJSONException(error_message=error_message)
            raise exception

        # Send response to client
        return self.pack_response_to_client(
            response=True,
            status_code=http_post_response.status_code,
            content=response_json,
            url=url)