From 6864ee8e34461a7f9ba8838ee05b64ffbaf28984 Mon Sep 17 00:00:00 2001 From: C1299593 Phillipe Smith Carvalho Chaves Date: Thu, 6 Aug 2020 09:56:21 -0300 Subject: [PATCH] Add many updates. More info on README.md#Changelog. --- .github/workflows/docker-publish.yml | 68 ++++++++++++++++++++++++++++ README.md | 5 +- rundeck_exporter.py | 31 +++++++------ 3 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..37835b1 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,68 @@ +name: Docker + +on: + push: + tags: + - v* + +env: + # TODO: Change variable to your image's name. + IMAGE_NAME: rundeck-exporter + +jobs: + # Run tests. + # See also https://docs.docker.com/docker-hub/builds/automated-testing/ + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Run tests + run: | + if [ -f docker-compose.test.yml ]; then + docker-compose --file docker-compose.test.yml build + docker-compose --file docker-compose.test.yml run sut + else + docker build . --file Dockerfile + fi + + # Push image to GitHub Packages. + # See also https://docs.docker.com/docker-hub/builds/ + push: + # Ensure test job passes before pushing image. + needs: test + + runs-on: ubuntu-latest + if: github.event_name == 'push' + + steps: + - uses: actions/checkout@v2 + + - name: Build image + run: docker build . --file Dockerfile --tag $IMAGE_NAME + + - name: Log into registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + + - name: Push image + run: | + IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION diff --git a/README.md b/README.md index 99683a0..749feba 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This exporter uses the prometheus_client and requests Python module to expose Ru * RUNDECK_URL/api/*version*/system/info * RUNDECK_URL/api/*version*/metrics/metrics - Where *version* represents the Rundeck API version, like: 29,30,31,etc. + Where *version* represents the Rundeck API version, like: 31,32,33,34,etc. This code was tested on Rundeck API version 31. @@ -160,7 +160,8 @@ docker run --rm -d -p 9620:9620 rundeck_exporter \ * --rundeck.projects.filter: Get executions only from listed projects (delimiter = space) * --rundeck.projects.executions.limit: Limit project executions metrics query. Default: 20 * --rundeck.cached.requests.ttl: Rundeck cached requests (by now, only for rundeck.projects.executions) expiration time. Default: 120 -* Add log messages through logging module * Add code improvements * Add cachetools to pip install on Dockerfile +* Add logging module to replace print calls +* Add better error handling * Change args location, now located at class RundeckMetricsCollector diff --git a/rundeck_exporter.py b/rundeck_exporter.py index 8598b91..53a927a 100755 --- a/rundeck_exporter.py +++ b/rundeck_exporter.py @@ -104,13 +104,16 @@ def __init__(self): self.rundeck_node = '' if not self.args.rundeck_url or not self.args.rundeck_token: - self.exit_with_msg('Rundeck URL and Token are required.') + self.exit_with_msg(msg='Rundeck URL and Token are required.', level='critical') """ Method to manage requests on Rundeck API Endpoints """ def request_data_from(self, endpoint: str) -> dict: - response = requests.get( + response = None + + try: + response = requests.get( f'{self.args.rundeck_url}/api/{self.args.rundeck_api_version}/{endpoint}', headers={ 'Accept': 'application/json', @@ -118,17 +121,15 @@ def request_data_from(self, endpoint: str) -> dict: }, verify=not self.args.rundeck_skip_ssl ) + response_json = response.json() + self.client_requests_count += 1 - try: - if response: - self.client_requests_count += 1 - return response.json() - except requests.exceptions.SSLError: - self.exit_with_msg('SSL Certificate Verify Failed.') - except (OSError, requests.exceptions.ConnectionError): - self.exit_with_msg('Connection error.') + if response_json and response_json.get('error') == True: + raise Exception(response_json.get('message')) + + return response_json except Exception as error: - self.exit_with_msg(response.text if response.text else str(error)) + self.exit_with_msg(msg=response.text if response else str(error), level='critical') @cached(cache=TTLCache(maxsize=1024, ttl=args.rundeck_cached_requests_ttl)) def cached_request_data_from(self, endpoint: str) -> dict: @@ -259,6 +260,7 @@ def get_counters(self, metrics: dict): Method to collect Rundeck metrics """ def collect(self): + self.client_requests_count = 0 metrics = self.request_data_from('/metrics/metrics') system_info = self.request_data_from('/system/info') self.rundeck_node = system_info['system']['rundeck']['node'] @@ -299,8 +301,9 @@ def collect(self): yield(execution) @staticmethod - def exit_with_msg(msg: str): - raise SystemExit(f'\nError:\n {msg}\n') + def exit_with_msg(msg: str, level: str): + getattr(logging, level)(msg) + exit(getattr(logging, level.upper())) @classmethod def run(cls): @@ -312,7 +315,7 @@ def run(cls): while True: sleep(1) except OSError as os_error: - cls.exit_with_msg(str(os_error)) + cls.exit_with_msg(msg=str(os_error), level='critical') if __name__ == "__main__":