summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSalim Fadhley <sal@stodge.org>2014-08-02 01:15:34 +0100
committerSalim Fadhley <sal@stodge.org>2014-08-02 01:15:34 +0100
commita4c3fab827673da3c70e834ffd4d362f24190de1 (patch)
treecf14737ee07f7c19590b564b0669e31bd73813a9
parent53ca463e6dad71352608b19224cecad06df5858e (diff)
downloadpython-jenkinsapi-a4c3fab827673da3c70e834ffd4d362f24190de1.tar.gz
python-jenkinsapi-a4c3fab827673da3c70e834ffd4d362f24190de1.tar.bz2
python-jenkinsapi-a4c3fab827673da3c70e834ffd4d362f24190de1.zip
reducing the number of errors from the refactor of invocation/queues
-rw-r--r--jenkinsapi/custom_exceptions.py7
-rw-r--r--jenkinsapi/jenkinsbase.py7
-rw-r--r--jenkinsapi/job.py98
-rw-r--r--jenkinsapi/queue.py70
-rw-r--r--jenkinsapi/utils/requester.py12
-rw-r--r--jenkinsapi_tests/systests/job_configs.py2
-rw-r--r--jenkinsapi_tests/systests/test_invocation.py72
-rw-r--r--jenkinsapi_tests/systests/test_queue.py1
8 files changed, 155 insertions, 114 deletions
diff --git a/jenkinsapi/custom_exceptions.py b/jenkinsapi/custom_exceptions.py
index ce536fa..f3bf286 100644
--- a/jenkinsapi/custom_exceptions.py
+++ b/jenkinsapi/custom_exceptions.py
@@ -66,6 +66,13 @@ class NoBuildData(NotFound):
pass
+class NotBuiltYet(NotFound):
+ """
+ A job has no build data.
+ """
+ pass
+
+
class ArtifactBroken(JenkinsAPIException):
"""
An artifact is broken, wrong
diff --git a/jenkinsapi/jenkinsbase.py b/jenkinsapi/jenkinsbase.py
index 6287dfb..d0974dc 100644
--- a/jenkinsapi/jenkinsbase.py
+++ b/jenkinsapi/jenkinsbase.py
@@ -3,6 +3,7 @@ Module for JenkinsBase class
"""
import ast
+import pprint
import logging
from jenkinsapi import config
from jenkinsapi.custom_exceptions import JenkinsAPIException
@@ -54,6 +55,7 @@ class JenkinsBase(object):
self._data = self._poll()
if 'jobs' in self._data:
self._data['jobs'] = self.resolve_job_folders(self._data['jobs'])
+ return self
def _poll(self):
url = self.python_api_url(self.baseurl)
@@ -71,6 +73,11 @@ class JenkinsBase(object):
logging.exception('Inappropriate content found at %s', url)
raise JenkinsAPIException('Cannot parse %s' % response.content)
+ def pprint(self):
+ """Print out all the data in this object for debugging.
+ """
+ pprint.pprint(self._data)
+
def resolve_job_folders(self, jobs):
for job in list(jobs):
if 'color' not in job.keys():
diff --git a/jenkinsapi/job.py b/jenkinsapi/job.py
index b0ceb48..9f79529 100644
--- a/jenkinsapi/job.py
+++ b/jenkinsapi/job.py
@@ -15,7 +15,7 @@ import xml.etree.ElementTree as ET
from collections import defaultdict
from time import sleep
from jenkinsapi.build import Build
-from jenkinsapi.invocation import Invocation
+from jenkinsapi.queue import QueueItem
from jenkinsapi.jenkinsbase import JenkinsBase
from jenkinsapi.queue import QueueItem
from jenkinsapi.mutable_jenkins_thing import MutableJenkinsThing
@@ -45,6 +45,7 @@ class Job(JenkinsBase, MutableJenkinsThing):
Represents a jenkins job
A job can hold N builds which are the actual execution environments
"""
+
def __init__(self, url, name, jenkins_obj):
self.name = name
self.jenkins = jenkins_obj
@@ -107,7 +108,8 @@ class Job(JenkinsBase, MutableJenkinsThing):
and updates it with the missing builds if needed.'''
if not data.get("builds"):
return data
- # do not call _buildid_for_type here: it would poll and do an infinite loop
+ # do not call _buildid_for_type here: it would poll and do an infinite
+ # loop
oldest_loaded_build_number = data["builds"][-1]["number"]
if not data['firstBuild']:
first_build_number = oldest_loaded_build_number
@@ -117,7 +119,8 @@ class Job(JenkinsBase, MutableJenkinsThing):
if all_builds_loaded:
return data
api_url = self.python_api_url(self.baseurl)
- response = self.get_data(api_url, params={'tree': 'allBuilds[number,url]'})
+ response = self.get_data(
+ api_url, params={'tree': 'allBuilds[number,url]'})
data['builds'] = response['allBuilds']
return data
@@ -170,74 +173,39 @@ class Job(JenkinsBase, MutableJenkinsThing):
assert isinstance(block, bool)
assert isinstance(skip_if_running, bool)
- # Create a new invocation instance
- invocation = Invocation(self)
# Either copy the params dict or make a new one.
build_params = build_params and dict(
build_params.items()) or {} # Via POSTed JSON
params = {} # Via Get string
- with invocation:
- if len(self.get_params_list()) == 0:
- if self.is_queued():
- raise WillNotBuild('%s is already queued' % repr(self))
-
- elif self.is_running():
- if skip_if_running:
- log.warn(
- "Will not request new build because %s is already running", self.name)
- else:
- log.warn(
- "Will re-schedule %s even though it is already running", self.name)
- elif self.has_queued_build(build_params):
- msg = 'A build with these parameters is already queued.'
- raise WillNotBuild(msg)
-
- log.info("Attempting to start %s on %s", self.name, repr(
- self.get_jenkins_obj()))
-
- url = self.get_build_triggerurl()
- # If job has file parameters - it must be triggered
- # using "/build", not by "/buildWithParameters"
- # "/buildWithParameters" will ignore non-file parameters
- if files:
- url = "%s/build" % self.baseurl
-
- if cause:
- build_params['cause'] = cause
-
- if securitytoken:
- params['token'] = securitytoken
-
- build_params['json'] = self.mk_json_from_build_parameters(build_params, files)
- data = build_params
-
- response = self.jenkins.requester.post_and_confirm_status(
- url,
- data=data,
- params=params,
- files=files,
- valid=[200, 201]
- )
- response = response
- if invoke_pre_check_delay > 0:
- log.info(
- "Waiting for %is to allow Jenkins to catch up", invoke_pre_check_delay)
- sleep(invoke_pre_check_delay)
- if block:
- total_wait = 0
-
- while self.is_queued():
- log.info(
- "Waited %is for %s to begin...", total_wait, self.name)
- sleep(invoke_block_delay)
- total_wait += invoke_block_delay
- if self.is_running():
- running_build = self.get_last_build()
- running_build.block_until_complete(
- delay=invoke_pre_check_delay)
- return invocation
+ url = self.get_build_triggerurl()
+ # If job has file parameters - it must be triggered
+ # using "/build", not by "/buildWithParameters"
+ # "/buildWithParameters" will ignore non-file parameters
+ if files:
+ url = "%s/build" % self.baseurl
+
+ if cause:
+ build_params['cause'] = cause
+
+ if securitytoken:
+ params['token'] = securitytoken
+
+ build_params['json'] = self.mk_json_from_build_parameters(
+ build_params, files)
+ data = build_params
+
+ response = self.jenkins.requester.post_url(
+ url,
+ data=data,
+ params=params,
+ files=files,
+ )
+
+ queue_url = response.headers['location']
+ qi = QueueItem(queue_url, self.jenkins)
+ return qi
def _buildid_for_type(self, buildtype):
"""Gets a buildid for a given type of build"""
diff --git a/jenkinsapi/queue.py b/jenkinsapi/queue.py
index d0efc5b..967ccf0 100644
--- a/jenkinsapi/queue.py
+++ b/jenkinsapi/queue.py
@@ -3,8 +3,9 @@ Queue module for jenkinsapi
"""
from jenkinsapi.jenkinsbase import JenkinsBase
-from jenkinsapi.custom_exceptions import UnknownQueueItem
+from jenkinsapi.custom_exceptions import UnknownQueueItem, NotBuiltYet
import logging
+import time
log = logging.getLogger(__name__)
@@ -31,7 +32,9 @@ class Queue(JenkinsBase):
def iteritems(self):
for item in self._data['items']:
- yield item['id'], QueueItem(self.jenkins, **item)
+ id = item['id']
+ item_baseurl = "%s/item/%i" % (self.baseurl, id)
+ yield item['id'], QueueItem(baseurl=item_baseurl, jenkins_obj=self.jenkins)
def iterkeys(self):
for item in self._data['items']:
@@ -74,15 +77,21 @@ class Queue(JenkinsBase):
self.get_jenkins_obj().requester.post_url(deleteurl)
-class QueueItem(object):
- """
- Flexible class to handle queue items.
- If the Jenkins API changes this support those changes
+class QueueItem(JenkinsBase):
+ """An individual item in the queue
"""
- def __init__(self, jenkins, **kwargs):
- self.jenkins = jenkins
- self.__dict__.update(kwargs)
+ def __init__(self, baseurl, jenkins_obj):
+ self.jenkins = jenkins_obj
+ JenkinsBase.__init__(self, baseurl)
+
+ @property
+ def id(self):
+ return self._data['id']
+
+
+ def get_jenkins_obj(self):
+ return self.jenkins
def get_job(self):
"""
@@ -105,4 +114,45 @@ class QueueItem(object):
self.__class__.__name__, str(self))
def __str__(self):
- return "%s #%i" % (self.task['name'], self.id)
+ return "%s Queue #%i" % (self._data['task']['name'], self._data['id'])
+
+ def get_build(self):
+ build_number = self.get_build_number()
+ job_name = self.get_job_name()
+ return self.jenkins[job_name][build_number]
+
+
+ def block_until_complete(self, delay=15):
+ build = self.block_until_building(delay)
+ return build.block_until_complete(delay=delay)
+
+
+ def block_until_building(self, delay=5):
+ while True:
+ try:
+ return self.poll().get_build()
+ except NotBuiltYet:
+ time.sleep(delay)
+ continue
+
+
+ def is_running(self):
+ """Return True if this queued item is running.
+ """
+ try:
+ return self.get_build().is_running()
+ except NotBuiltYet:
+ return False
+
+ def get_build_number(self):
+ try:
+ return self._data['executable']['number']
+ except KeyError:
+ raise NotBuiltYet()
+
+ def get_job_name(self):
+ try:
+ return self._data['task']['name']
+ except KeyError:
+ raise NotBuiltYet()
+ \ No newline at end of file
diff --git a/jenkinsapi/utils/requester.py b/jenkinsapi/utils/requester.py
index e818f7a..50e077b 100644
--- a/jenkinsapi/utils/requester.py
+++ b/jenkinsapi/utils/requester.py
@@ -46,8 +46,8 @@ class Requester(object):
self.password = password
self.ssl_verify = ssl_verify
- def get_request_dict(self, params=None, data=None, files=None, headers=None):
- requestKwargs = {}
+ def get_request_dict(self, params=None, data=None, files=None, headers=None, **kwargs):
+ requestKwargs = kwargs
if self.username:
requestKwargs['auth'] = (self.username, self.password)
@@ -90,12 +90,12 @@ class Requester(object):
)
return url
- def get_url(self, url, params=None, headers=None):
- requestKwargs = self.get_request_dict(params=params, headers=headers)
+ def get_url(self, url, params=None, headers=None, allow_redirects=True):
+ requestKwargs = self.get_request_dict(params=params, headers=headers, allow_redirects=allow_redirects)
return requests.get(self._update_url_scheme(url), **requestKwargs)
- def post_url(self, url, params=None, data=None, files=None, headers=None):
- requestKwargs = self.get_request_dict(params=params, data=data, files=files, headers=headers)
+ def post_url(self, url, params=None, data=None, files=None, headers=None, allow_redirects=True):
+ requestKwargs = self.get_request_dict(params=params, data=data, files=files, headers=headers, allow_redirects=allow_redirects)
return requests.post(self._update_url_scheme(url), **requestKwargs)
def post_xml_and_confirm_status(self, url, params=None, data=None, valid=None):
diff --git a/jenkinsapi_tests/systests/job_configs.py b/jenkinsapi_tests/systests/job_configs.py
index 2f127a4..c578121 100644
--- a/jenkinsapi_tests/systests/job_configs.py
+++ b/jenkinsapi_tests/systests/job_configs.py
@@ -61,7 +61,7 @@ SHORTISH_JOB = """
<concurrentBuild>false</concurrentBuild>
<builders>
<hudson.tasks.Shell>
- <command>ping -c 10 localhost</command>
+ <command>ping -c 5 localhost</command>
</hudson.tasks.Shell>
</builders>
<publishers/>
diff --git a/jenkinsapi_tests/systests/test_invocation.py b/jenkinsapi_tests/systests/test_invocation.py
index 9c0c230..aaf74c4 100644
--- a/jenkinsapi_tests/systests/test_invocation.py
+++ b/jenkinsapi_tests/systests/test_invocation.py
@@ -7,75 +7,83 @@ try:
except ImportError:
import unittest
import time
+import logging
from jenkinsapi.build import Build
-from jenkinsapi.invocation import Invocation
+from jenkinsapi.queue import QueueItem
from jenkinsapi_tests.systests.base import BaseSystemTest
from jenkinsapi_tests.test_utils.random_strings import random_string
from jenkinsapi_tests.systests.job_configs import LONG_RUNNING_JOB
from jenkinsapi_tests.systests.job_configs import SHORTISH_JOB, EMPTY_JOB
+log = logging.getLogger(__name__)
+
+
class TestInvocation(BaseSystemTest):
def test_invocation_object(self):
job_name = 'create_%s' % random_string()
- job = self.jenkins.create_job(job_name, LONG_RUNNING_JOB)
- ii = job.invoke(invoke_pre_check_delay=7)
- self.assertIsInstance(ii, Invocation)
+ job = self.jenkins.create_job(job_name, SHORTISH_JOB)
+ qq = job.invoke(invoke_pre_check_delay=7)
+ self.assertIsInstance(qq, QueueItem)
# Let Jenkins catchup
- time.sleep(3)
- self.assertTrue(ii.is_queued_or_running())
- self.assertEquals(ii.get_build_number(), 1)
+ qq.block_until_building()
+ self.assertEquals(qq.get_build_number(), 1)
def test_get_block_until_build_running(self):
job_name = 'create_%s' % random_string()
job = self.jenkins.create_job(job_name, LONG_RUNNING_JOB)
- ii = job.invoke(invoke_pre_check_delay=7)
+ qq = job.invoke(invoke_pre_check_delay=7)
time.sleep(3)
- bn = ii.get_build_number()
+ bn = qq.block_until_building(delay=3).get_number()
self.assertIsInstance(bn, int)
- ii.block(until='not_queued')
- self.assertTrue(ii.is_running())
- b = ii.get_build()
+
+ b = qq.get_build()
self.assertIsInstance(b, Build)
- ii.stop()
- self.assertFalse(ii.is_running())
- self.assertIsInstance(ii.get_build().get_console(), str)
- self.assertIn('Started by user', ii.get_build().get_console())
-
+ self.assertTrue(b.is_running())
+
+ b.stop()
+ time.sleep(1)
+ self.assertFalse(b.poll().is_running())
+ console = b.get_console()
+ self.assertIsInstance(console, str)
+ self.assertIn('Started by user', console)
+
def test_get_block_until_build_complete(self):
job_name = 'create_%s' % random_string()
job = self.jenkins.create_job(job_name, SHORTISH_JOB)
- ii = job.invoke()
- ii.block(until='completed')
- self.assertFalse(ii.is_running())
-
+ qq = job.invoke()
+ qq.block_until_complete()
+ self.assertFalse(qq.get_build().is_running())
+
def test_multiple_invocations_and_get_last_build(self):
job_name = 'create_%s' % random_string()
-
+
job = self.jenkins.create_job(job_name, SHORTISH_JOB)
-
+
for _ in range(3):
ii = job.invoke()
- ii.block(until='completed')
-
+ ii.block_until_complete(delay=2)
+
build_number = job.get_last_good_buildnumber()
self.assertEquals(build_number, 3)
-
+
build = job.get_build(build_number)
self.assertIsInstance(build, Build)
-
+
def test_multiple_invocations_and_get_build_number(self):
job_name = 'create_%s' % random_string()
-
+
job = self.jenkins.create_job(job_name, EMPTY_JOB)
-
+
for invocation in range(3):
- ii = job.invoke()
- ii.block(until='completed')
- build_number = ii.get_build_number()
+ qq = job.invoke()
+ qq.block_until_complete(delay=1)
+ build_number = qq.get_build_number()
self.assertEquals(build_number, invocation + 1)
if __name__ == '__main__':
+# logging.basicConfig()
+# logging.getLogger("").setLevel(logging.INFO)
unittest.main()
diff --git a/jenkinsapi_tests/systests/test_queue.py b/jenkinsapi_tests/systests/test_queue.py
index de95684..e53cf38 100644
--- a/jenkinsapi_tests/systests/test_queue.py
+++ b/jenkinsapi_tests/systests/test_queue.py
@@ -38,6 +38,7 @@ class TestQueue(BaseSystemTest):
self.assertTrue(j.is_queued_or_running())
queue = self.jenkins.get_queue()
+
reprString = repr(queue)
self.assertIn(queue.baseurl, reprString)