Posts are not yet working.
About this library
-------------------
-Jenkins is the market leading continuous integration system, originally created by Kohsuke Kawaguchi. This API makes Jenkins even easier to use by providing an easy to use conventional python interface.
+Jenkins is the market leading continuous integration system, originally created by Kohsuke Kawaguchi.
-Jenkins (and It's predecessor Hudson) are useful projects for automating common development tasks (e.g. unit-testing, production batches) - but they are somewhat Java-centric. Thankfully the designers have provided an excellent and complete REST interface. This library wraps up that interface as more conventional python objects in order to make most Jenkins oriented tasks simpler.
+Jenkins (and It's predecessor Hudson) are useful projects for automating common development tasks (e.g. unit-testing, production batches) - but they are somewhat Java-centric. Thankfully the designers have provided an excellent and complete REST interface. This library wraps up that interface as more conventional python objects in order to make many Jenkins oriented tasks easier to automate.
This library can help you:
* Install artefacts to custom-specified directory structures
* username/password auth support for jenkins instances with auth turned on
* Ability to search for builds by subversion revision
- * Ability to add/remove/query jenkins slaves
+ * Ability to add/remove/query Jenkins slaves
+ * Ability to add/remove/modify Jenkins views
Important Links
----------------
>>> import jenkinsapi
>>> from jenkinsapi.jenkins import Jenkins
>>> J = Jenkins('http://localhost:8080')
- >>> J.keys()
+ >>> J.keys() # Jenkins objects appear to be dict-like, mapping keys (job-names) to
['foo', 'test_jenkinsapi']
>>> J['test_jenkinsapi']
<jenkinsapi.job.Job test_jenkinsapi>
--- /dev/null
+from jenkinsapi.jenkins import Jenkins
+J = Jenkins('http://localhost:8080')
+
+EMPTY_JOB_CONFIG = '''\
+<?xml version='1.0' encoding='UTF-8'?>
+<project>
+ <actions/>
+ <description></description>
+ <keepDependencies>false</keepDependencies>
+ <properties/>
+ <scm class="hudson.scm.NullSCM"/>
+ <canRoam>true</canRoam>
+ <disabled>false</disabled>
+ <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
+ <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
+ <triggers class="vector"/>
+ <concurrentBuild>false</concurrentBuild>
+ <builders/>
+ <publishers/>
+ <buildWrappers/>
+</project>
+'''
+
+new_job = J.create_job(jobname='foo_job', config=EMPTY_JOB_CONFIG)
+
+j= J['foo_job']
+print j
\ No newline at end of file
+"""
+This module is a collection of helpful, high-level functions for automating common tasks.
+Many of these functions were designed to be exposed to the command-line, hence the have simple string arguments.
+"""
from jenkinsapi.artifact import Artifact
from jenkinsapi import constants
from jenkinsapi.jenkins import Jenkins
-from jenkinsapi.exceptions import UnknownJob, NotAuthorized
-from jenkinsapi.fingerprint import Fingerprint
-from jenkinsapi.jenkinsbase import JenkinsBase
-from jenkinsapi.job import Job
-from jenkinsapi.node import Node
-from jenkinsapi.queue import Queue
-from jenkinsapi.view import View
-from jenkinsapi import config
-from utils.urlopener import mkurlopener, mkopener, NoAuto302Handler
-import cookielib
-import logging
import time
import urllib
import urllib2
+import logging
import urlparse
+import requests
+import StringIO
+import cookielib
+from utils.urlopener import mkurlopener, mkopener, NoAuto302Handler
+
+from jenkinsapi import config
+from jenkinsapi.job import Job
+from jenkinsapi.node import Node
+from jenkinsapi.queue import Queue
+from jenkinsapi.view import View
+from jenkinsapi.fingerprint import Fingerprint
+from jenkinsapi.jenkinsbase import JenkinsBase
+from jenkinsapi.utils.requester import Requester
+from jenkinsapi.exceptions import UnknownJob, NotAuthorized
try:
import json
"""
Represents a jenkins environment.
"""
- def __init__(self, baseurl, username=None, password=None, proxyhost=None, proxyport=None, proxyuser=None, proxypass=None, formauth=False, krbauth=False):
+ def __init__(self, baseurl, username=None, password=None, requester=None):
"""
-
:param baseurl: baseurl for jenkins instance including port, str
:param username: username for jenkins auth, str
:param password: password for jenkins auth, str
- :param proxyhost: proxyhostname, str
- :param proxyport: proxyport, int
- :param proxyuser: proxyusername for proxy auth, str
- :param proxypass: proxypassword for proxyauth, str
:return: a Jenkins obj
"""
self.username = username
self.password = password
- self.proxyhost = proxyhost
- self.proxyport = proxyport
- self.proxyuser = proxyuser
- self.proxypass = proxypass
- JenkinsBase.__init__(self, baseurl, formauth=formauth, krbauth=krbauth)
+ self.requester = requester or Requester(username, password)
+ JenkinsBase.__init__(self, baseurl)
def _clone(self):
- return Jenkins(self.baseurl, username=self.username,
- password=self.password, proxyhost=self.proxyhost,
- proxyport=self.proxyport, proxyuser=self.proxyuser,
- proxypass=self.proxypass, formauth=self.formauth, krbauth=self.krbauth)
-
- def get_proxy_auth(self):
- return self.proxyhost, self.proxyport, self.proxyuser, self.proxypass
-
- def get_jenkins_auth(self):
- return self.username, self.password, self.baseurl
-
- def get_auth(self):
- auth_args = []
- auth_args.extend(self.get_jenkins_auth())
- auth_args.extend(self.get_proxy_auth())
- log.debug("auth_args: %s" % auth_args)
- return auth_args
+ return Jenkins(self.baseurl, username=self.username, password=self.password, requester=self.requester)
def get_base_server_url(self):
return self.baseurl[:-(len(config.JENKINS_API))]
- def get_opener(self):
- if self.formauth:
- return self.get_login_opener()
- if self.krbauth:
- return self.get_krb_opener()
- return mkurlopener(*self.get_auth())
-
- def get_login_opener(self):
- hdrs = []
- if getattr(self, '_cookies', False):
- mcj = cookielib.MozillaCookieJar()
- for c in self._cookies:
- mcj.set_cookie(c)
- hdrs.append(urllib2.HTTPCookieProcessor(mcj))
- return mkopener(*hdrs)
-
def get_krb_opener(self):
if not mkkrbopener:
raise NotImplementedError('JenkinsAPI was installed without Kerberos support.')
return mkkrbopener(self.baseurl)
- def login(self):
- formdata = dict(j_username=self.username, j_password=self.password,
- remember_me=True, form='/')
- formdata.update(dict(json=json.dumps(formdata), Submit='log in'))
- formdata = urllib.urlencode(formdata)
-
- loginurl = urlparse.urljoin(self.baseurl, 'j_acegi_security_check')
- mcj = cookielib.MozillaCookieJar()
- cookiehandler = urllib2.HTTPCookieProcessor(mcj)
-
- urlopen = mkopener(NoAuto302Handler, cookiehandler)
- res = urlopen(loginurl, data=formdata)
- self._cookies = [c for c in mcj]
- return res.getcode() == 302
-
def validate_fingerprint(self, id):
obj_fingerprint = Fingerprint(self.baseurl, id, jenkins_obj=self)
obj_fingerprint.validate()
:return: new Job obj
"""
headers = {'Content-Type': 'text/xml'}
- qs = urllib.urlencode({'name': jobname})
- url = urlparse.urljoin(self.baseurl, "createItem?%s" % qs)
- request = urllib2.Request(url, config, headers)
- self.post_data(request, None)
+ params = {'name': jobname}
+ self.requester.hit_url(self.baseurl, data=config, params=params, headers=headers)
newjk = self._clone()
return newjk.get_job(jobname)
newjk = self._clone()
return newjk.get_job(newjobname)
- def iteritems(self):
- return self.get_jobs()
-
def iterkeys(self):
for info in self._data["jobs"]:
yield info["name"]
+ def iteritems(self):
+ """
+ :param return: An iterator of pairs. Each pair will be (job name, Job object)
+ """
+ return self.get_jobs()
+
+ def items(self):
+ """
+ :param return: A list of pairs. Each pair will be (job name, Job object)
+ """
+ return list(self.get_jobs())
+
def keys(self):
return [ a for a in self.iterkeys() ]
def __str__(self):
raise NotImplemented
- def __init__(self, baseurl, poll=True, formauth=False, krbauth=False):
+ def __init__(self, baseurl, poll=True):
"""
Initialize a jenkins connection
"""
self.baseurl = baseurl
- self.formauth = formauth
- self.krbauth = krbauth
- if poll and not self.formauth:
+ if poll:
try:
self.poll()
- except urllib2.HTTPError, hte:
+ except urllib2.HTTPError, hte: #TODO: Wrong exception
log.exception(hte)
log.warn( "Failed to connect to %s" % baseurl )
raise
+ def __eq__(self, other):
+ """
+ Return true if the other object represents a connection to the same server
+ """
+ if not isinstance(other, self.__class__):
+ return False
+ if not other.baseurl == self.baseurl:
+ return False
+ return True
+
def poll(self):
self._data = self._poll()
def _poll(self):
url = self.python_api_url(self.baseurl)
- return retry_function(self.RETRY_ATTEMPTS , self.get_data, url)
-
- def get_jenkins_obj(self):
- """Not implemented, abstract method implemented by child classes"""
- raise NotImplemented("Abstract method, implemented by child classes")
+ requester = self.get_jenkins_obj().requester
+ content = retry_function(self.RETRY_ATTEMPTS , requester.hit_url, url)
+ try:
+ return eval(content)
+ except SyntaxError:
+ log.exception('Inappropriate content found at %s' % url)
@classmethod
def python_api_url(cls, url):
fmt="%s%s"
else:
fmt = "%s/%s"
- return fmt % (url, config.JENKINS_API)
-
- def get_data(self, url):
- """
- Find out how to connect, and then grab the data.
- """
- fn_urlopen = self.get_jenkins_obj().get_opener()
- try:
- stream = fn_urlopen(url)
- result = eval(stream.read())
- except urllib2.HTTPError, e:
- if e.code == 404:
- raise
- log.exception("Error reading %s" % url)
- raise
- return result
-
- def post_data(self, url, content):
- try:
- urlopen = self.get_jenkins_obj().get_opener()
- result = urlopen(url, data=content).read().strip()
- except urllib2.HTTPError:
- log.exception("Error post data %s" % url)
- raise
- return result
-
- def hit_url(self, url, params = None):
- fn_urlopen = self.get_jenkins_obj().get_opener()
- try:
- if params: stream = fn_urlopen( url, urllib.urlencode(params) )
- else: stream = fn_urlopen( url )
- html_result = stream.read()
- except urllib2.HTTPError:
- log.exception("Error reading %s" % url)
- raise
- return html_result
+ return fmt % (url, config.JENKINS_API)
\ No newline at end of file
--- /dev/null
+import StringIO
+import requests
+
+class Requester(object):
+
+ """
+ A class which carries out HTTP requests. You can replace this class with one of your own implementation if you require
+ some other way to access Jenkins.
+
+ This default class can handle simple authentication only.
+ """
+
+ def __init__(self, username=None, password=None):
+ if username:
+ assert password, 'Cannot set a username without a password!'
+
+ self.username = None
+ self.password = None
+
+ def hit_url(self, url, params=None, data=None, headers=None):
+ requestKwargs = {}
+ if self.username:
+ requestKwargs['auth'] = (self.username, self.password)
+
+ if params:
+ assert isinstance(params, dict), 'Params must be a dict, got %s' % repr(params)
+ requestKwargs['params'] = params
+
+ if headers:
+ assert isinstance(headers, dict), 'headers must be a dict, got %s' % repr(headers)
+ requestKwargs['headers'] = headers
+
+ if data:
+ requestKwargs['data'] = data
+ response = requests.post(url, **requestKwargs)
+ else:
+ response = requests.get(url, **requestKwargs)
+
+ import ipdb; ipdb.set_trace()
+
+ return response.text
def tearDownPackage():
- launcher.stop()
+ launcher.stop()
\ No newline at end of file
--- /dev/null
+import mock
+import unittest
+import datetime
+
+from jenkinsapi.jenkins import Jenkins
+
+class TestJenkins(unittest.TestCase):
+
+ DATA = {}
+
+ @mock.patch.object(Jenkins, '_poll')
+ def setUp(self, _poll):
+ _poll.return_value = self.DATA
+ self.J = Jenkins('http://localhost:8080', username='foouser', password='foopassword')
+
+ def testClone(self):
+ JJ = self.J._clone()
+ self.assertNotEquals(id(JJ), id(self.J))
+ self.assertEquals(JJ, self.J)
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
{
"name":"Virtualenv 2.7",
- "working_dir": "${project_path:${folder}}/src",
+ //"working_dir": "${project_path:${folder}}/src",
"cmd":
[
"${project_path}/bin/python2.7",
packages=['jenkinsapi', 'jenkinsapi.utils', 'jenkinsapi.command_line', 'jenkinsapi_tests'],
zip_safe=True,
include_package_data=False,
- install_requires=[],
+ install_requires=['requests==1.2.3'],
test_suite='jenkinsapi_tests',
tests_require=['mock', 'nose'],
extras_require={