diff --git a/cabot/cabotapp/jenkins.py b/cabot/cabotapp/jenkins.py index 43f91b4..6e469cf 100644 --- a/cabot/cabotapp/jenkins.py +++ b/cabot/cabotapp/jenkins.py @@ -1,13 +1,15 @@ from datetime import datetime -import requests +from jenkinsapi.jenkins import Jenkins from celery.utils.log import get_task_logger from django.conf import settings from django.utils import timezone logger = get_task_logger(__name__) -auth = (settings.JENKINS_USER, settings.JENKINS_PASS) + +def _get_jenkins_client(): + return Jenkins(settings.JENKINS_API, username=settings.JENKINS_USER, password=settings.JENKINS_PASS) def get_job_status(jobname): @@ -17,19 +19,18 @@ def get_job_status(jobname): 'blocked_build_time': None, 'status_code': 200 } - endpoint = settings.JENKINS_API + 'job/%s/api/json' % jobname - resp = requests.get(endpoint, auth=auth, verify=True) - status = resp.json() - ret['status_code'] = resp.status_code - ret['job_number'] = status['lastBuild'].get('number', None) - if status['color'].startswith('blue') or status['color'].startswith('green'): # Jenkins uses "blue" for successful; Hudson uses "green" - ret['active'] = True - ret['succeeded'] = True - elif status['color'] == 'disabled': - ret['active'] = False - ret['succeeded'] = False - if status['queueItem'] and status['queueItem']['blocked']: + client = _get_jenkins_client() + + job = client.get_job(jobname) + last_build = job.get_last_build() + + ret['job_number'] = last_build.get_number() + ret['active'] = job.is_enabled() + ret['succeeded'] = (job.is_enabled()) and last_build.is_good() + + if job.is_queued(): + in_queued_since = job._data['queueItem']['inQueueSince'] # job.get_queue_item() crashes time_blocked_since = datetime.utcfromtimestamp( - float(status['queueItem']['inQueueSince']) / 1000).replace(tzinfo=timezone.utc) + float(in_queued_since) / 1000).replace(tzinfo=timezone.utc) ret['blocked_build_time'] = (timezone.now() - time_blocked_since).total_seconds() return ret diff --git a/cabot/cabotapp/tests/tests_basic.py b/cabot/cabotapp/tests/tests_basic.py index e07fc3f..1b93a05 100644 --- a/cabot/cabotapp/tests/tests_basic.py +++ b/cabot/cabotapp/tests/tests_basic.py @@ -420,6 +420,7 @@ class TestCheckRun(LocalTestCase): @patch('cabot.cabotapp.models.requests.get', throws_timeout) def test_timeout_handling_in_jenkins(self): + """This works because we are effectively patching requests.get globally, including in jenkinsapi.""" checkresults = self.jenkins_check.statuscheckresult_set.all() self.assertEqual(len(checkresults), 0) custom_check_types = add_custom_check_plugins() diff --git a/cabot/cabotapp/tests/tests_jenkins.py b/cabot/cabotapp/tests/tests_jenkins.py new file mode 100644 index 0000000..cf81fe0 --- /dev/null +++ b/cabot/cabotapp/tests/tests_jenkins.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +import unittest +from freezegun import freeze_time +from mock import patch, create_autospec +from cabot.cabotapp import jenkins +from django.utils import timezone +from datetime import timedelta +import jenkinsapi + +class TestGetStatus(unittest.TestCase): + + def setUp(self): + self.mock_build = create_autospec(jenkinsapi.build.Build) + self.mock_build.get_number.return_value = 12 + + self.mock_job = create_autospec(jenkinsapi.job.Job) + self.mock_job.is_enabled.return_value = True + self.mock_job.get_last_build.return_value = self.mock_build + + self.mock_client = create_autospec(jenkinsapi.jenkins.Jenkins) + self.mock_client.get_job.return_value = self.mock_job + + @patch("cabot.cabotapp.jenkins._get_jenkins_client") + def test_job_passing(self, mock_jenkins): + mock_jenkins.return_value = self.mock_client + + self.mock_build.is_good.return_value = True + self.mock_job.is_queued.return_value = False + + status = jenkins.get_job_status('foo') + + expected = { + 'active': True, + 'succeeded': True, + 'job_number': 12, + 'blocked_build_time': None, + 'status_code': 200 + } + self.assertEqual(status, expected) + + @patch("cabot.cabotapp.jenkins._get_jenkins_client") + def test_job_failing(self, mock_jenkins): + mock_jenkins.return_value = self.mock_client + + self.mock_build.is_good.return_value = False + self.mock_job.is_queued.return_value = False + + status = jenkins.get_job_status('foo') + + expected = { + 'active': True, + 'succeeded': False, + 'job_number': 12, + 'blocked_build_time': None, + 'status_code': 200 + } + self.assertEqual(status, expected) + + @freeze_time('2017-03-02 10:30') + @patch("cabot.cabotapp.jenkins._get_jenkins_client") + def test_job_queued(self, mock_jenkins): + mock_jenkins.return_value = self.mock_client + + self.mock_build.is_good.return_value = True + self.mock_job.is_queued.return_value = True + self.mock_job._data = { + 'queueItem': { + 'inQueueSince': float(timezone.now().strftime('%s')) * 1000 + } + } + with freeze_time(timezone.now() + timedelta(minutes=10)): + status = jenkins.get_job_status('foo') + + expected = { + 'active': True, + 'succeeded': True, + 'job_number': 12, + 'blocked_build_time': 600, + 'status_code': 200 + } + self.assertEqual(status, expected) diff --git a/requirements.txt b/requirements.txt index 2b36707..966aa47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,6 +20,7 @@ gunicorn==19.7.1 httplib2==0.10.3 icalendar==3.11.3 itypes==1.1.0 +jenkinsapi==0.3.4 Jinja2==2.9.6 kombu==4.0.2 Markdown==2.6.8