From eabf11b6d9d7d55f6edeb80132417e8960cbe65e Mon Sep 17 00:00:00 2001 From: Ramon van Alteren Date: Wed, 4 Jan 2012 18:06:35 +0100 Subject: [PATCH] Adapted ruslans changes to allow revision->buildnr caching It's pretty dumb to rebuild this map every time it is requested, jobs now hold a cache which gets refreshed only if the user explicitly asks for it. In addition I added support for multiple builds on a single revision which is a long standing problem with the old jenkins api. If a user manually rebuilds the same revision we end up with multiple builds with the same rev. This confuses the deployscript, and deploys will fail due to that. This change opens up the possibility to deal with this more gracefully --- pyjenkinsci/artifact.py | 6 +++--- pyjenkinsci/job.py | 24 ++++++++++++++++-------- pyjenkinsci/utils/md5hash.py | 17 ----------------- pyjenkinsci/utils/urlopener.py | 42 +++++++++++++++++++++++++++++------------- 4 files changed, 48 insertions(+), 41 deletions(-) delete mode 100644 pyjenkinsci/utils/md5hash.py diff --git a/pyjenkinsci/artifact.py b/pyjenkinsci/artifact.py index c2143d8..2c20ff7 100644 --- a/pyjenkinsci/artifact.py +++ b/pyjenkinsci/artifact.py @@ -5,11 +5,11 @@ import cStringIO import zipfile import cPickle import datetime +import hashlib from pyjenkinsci import config from pyjenkinsci.utils.retry import retry_function from pyjenkinsci.exceptions import ArtifactBroken -from pyjenkinsci.utils.md5hash import new_digest log = logging.getLogger( __name__ ) @@ -69,7 +69,7 @@ class Artifact(object): """ Get the artifact as a stream """ - artifact_digest = new_digest() + artifact_digest = hashlib.md5() tmp_buffer = cStringIO.StringIO() if self.build: @@ -147,7 +147,7 @@ class Artifact(object): tmp_buffer_existing = cStringIO.StringIO() existingfile = open( fspath, "rb" ) tmp_buffer_existing.write( existingfile.read() ) - existing_digest = new_digest() + existing_digest = hashlib.md5() existing_digest.update(tmp_buffer_existing.getvalue()) existing_hexdigest = existing_digest.hexdigest() return existing_hexdigest diff --git a/pyjenkinsci/job.py b/pyjenkinsci/job.py index 76ce08e..a0d870b 100644 --- a/pyjenkinsci/job.py +++ b/pyjenkinsci/job.py @@ -128,11 +128,15 @@ class Job(JenkinsBase): def get_revision_dict(self): """ - Get dictionary of all revision:buildnumber available + Get dictionary of all revisions with a list of buildnumbers (int) that used that particular revision """ - if not self._data.has_key( "builds" ): - raise NoBuildData( repr(self) ) - return dict( ( self.get_build(a["number"] ).get_revision(), a["number"] ) for a in self._data["builds"] ) + revs = {} + if 'builds' not in self._data: + raise NoBuildData( repr(self)) + for buildnumber in self.get_build_ids(): + rev = self.get_build(buildnumber).get_revision() + revs[rev] = revs.get(rev, []).append(buildnumber) + return revs def get_build_ids(self): """ @@ -161,15 +165,19 @@ class Job(JenkinsBase): bn = self.get_last_completed_buildnumber() return self.get_build( bn ) - def get_buildnumber_for_revision(self, revision ): + def get_buildnumber_for_revision(self, revision, refresh=False): """ - Returns the buildnumber for a revision + + :param revision: subversion revision to look for, int + :param refresh: boolean, whether or not to refresh the revision -> buildnumber map + :return: list of buildnumbers, [int] """ if not isinstance(revision, int): revision = int(revision) - revmap = self.get_revision_dict() + if self._revmap is None or refresh: + self._revmap = self.get_revision_dict() try: - return revmap[revision] + return self._revmap[revision] except KeyError: raise NotFound("Couldn't find a build with that revision") diff --git a/pyjenkinsci/utils/md5hash.py b/pyjenkinsci/utils/md5hash.py deleted file mode 100644 index 4fd85e8..0000000 --- a/pyjenkinsci/utils/md5hash.py +++ /dev/null @@ -1,17 +0,0 @@ -try: - import hashlib -except ImportError: - import md5 - - -def new_digest(): - if hashlib: - m = hashlib.md5() - else: - m = md5.new() - return m - -if __name__ == "__main__": - x = new_digest() - x.update("123") - print repr( x.digest() ) diff --git a/pyjenkinsci/utils/urlopener.py b/pyjenkinsci/utils/urlopener.py index 9d58807..5acf68e 100644 --- a/pyjenkinsci/utils/urlopener.py +++ b/pyjenkinsci/utils/urlopener.py @@ -6,24 +6,40 @@ import logging log = logging.getLogger( __name__ ) class PreemptiveBasicAuthHandler(urllib2.BaseHandler): - + """ + A BasicAuthHandler class that will add Basic Auth headers to a request + even when there is no basic auth challenge from the server + Jenkins does not challenge basic auth but expects it to be present + """ def __init__(self, password_mgr=None): - if password_mgr is None: - password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() - self.passwd = password_mgr - self.add_password = self.passwd.add_password + if password_mgr is None: + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + self.passwd = password_mgr + self.add_password = self.passwd.add_password def http_request(self,req): - uri = req.get_full_url() - user, pw = self.passwd.find_user_password(None,uri) - log.debug('ADDING REQUEST HEADER for uri (%s): %s:%s' % (uri,user,pw)) - if pw is None: return req - raw = "%s:%s" % (user, pw) - auth = 'Basic %s' % base64.b64encode(raw).strip() - req.add_unredirected_header('Authorization', auth) - return req + uri = req.get_full_url() + user, pw = self.passwd.find_user_password(None,uri) + log.debug('ADDING REQUEST HEADER for uri (%s): %s:%s' % (uri,user,pw)) + if pw is None: return req + raw = "%s:%s" % (user, pw) + auth = 'Basic %s' % base64.b64encode(raw).strip() + req.add_unredirected_header('Authorization', auth) + return req def mkurlopener( jenkinsuser, jenkinspass, jenkinsurl, proxyhost, proxyport, proxyuser, proxypass ): + """ + Creates an url opener that works with both jenkins auth and proxy auth + If no values are provided for the jenkins or proxy vars, a regular opener is returned + :param jenkinsuser: username for jenkins, str + :param jenkinspass: password for jenkins, str + :param jenkinsurl: jenkins url, str + :param proxyhost: proxy hostname, str + :param proxyport: proxy port, int + :param proxyuser: proxy username, str + :param proxypass: proxy password, str + :return: urllib2.opener configured for auth + """ handlers = [] for handler in get_jenkins_auth_handler(jenkinsuser=jenkinsuser, jenkinspass=jenkinspass, jenkinsurl=jenkinsurl): handlers.append(handler) -- 2.7.4