Mercurial > bitten > bitten-test
changeset 402:08801667f00f
Switch to urllib2 in slave so that we can support basic and digest authentication.
author | cmlenz |
---|---|
date | Fri, 03 Aug 2007 14:44:29 +0000 |
parents | a10942252ebc |
children | b187f0894838 |
files | bitten/master.py bitten/slave.py |
diffstat | 2 files changed, 50 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/bitten/master.py +++ b/bitten/master.py @@ -241,9 +241,11 @@ listener.build_completed(build) body = 'Build step processed' - req.send_response(200) + req.send_response(201) req.send_header('Content-Type', 'text/plain') req.send_header('Content-Length', str(len(body))) + req.send_header('Location', req.abs_href.builds(build.id, 'steps', + stepname)) req.write(body) raise RequestDone
--- a/bitten/slave.py +++ b/bitten/slave.py @@ -10,7 +10,7 @@ """Implementation of the build slave.""" from datetime import datetime -import httplib +import urllib2 import logging import os import platform @@ -19,7 +19,6 @@ except NameError: from sets import Set as set import shutil -import socket import tempfile import time import urlparse @@ -32,11 +31,23 @@ log = logging.getLogger('bitten.slave') +class SaneHTTPErrorProcessor(urllib2.HTTPErrorProcessor): + "The HTTPErrorProcessor defined in urllib needs some love." + + def http_response(self, request, response): + code, msg, hdrs = response.code, response.msg, response.info() + if code >= 300: + response = self.parent.error( + 'http', request, response, code, msg, hdrs) + return response + + class BuildSlave(object): """BEEP initiator implementation for the build slave.""" def __init__(self, url, name=None, config=None, dry_run=False, - work_dir=None, keep_files=False, single_build=False): + work_dir=None, keep_files=False, single_build=False, + username=None, password=None): """Create the build slave instance. @param url: The URL of the build master @@ -50,6 +61,9 @@ execution should be kept when done @param single_build: Whether this slave should exit after completing a single build, or continue processing builds forever + @param username: the username to use when authentication against the + build master is requested + @param password: the password to use when authentication is needed """ self.url = url if name is None: @@ -65,29 +79,29 @@ self.keep_files = keep_files self.single_build = single_build - def request(self, method, url, body=None, headers=None): - scheme, host, path, query, fragment = urlparse.urlsplit(url) - scheme = (scheme or 'http').lower() + self.opener = urllib2.build_opener(SaneHTTPErrorProcessor) + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + if username and password: + password_mgr.add_password(None, url, username, password) + self.opener.add_handler(urllib2.HTTPBasicAuthHandler(password_mgr)) + self.opener.add_handler(urllib2.HTTPDigestAuthHandler(password_mgr)) - if scheme == 'https': - conn = httplib.HTTPSConnection(host) - else: - conn = httplib.HTTPConnection(host) - - if headers is None: - headers = {} - if body is None: - body = '' - headers['Content-Length'] = len(body) - conn.request(method.upper(), path, body, headers) - return conn.getresponse() + def request(self, method, url, body=None, headers=None): + req = urllib2.Request(url, body, headers or {}) + try: + return self.opener.open(req) + except urllib2.HTTPError, e: + if e.code >= 300: + log.warn('Server returned error %d: %s', e.code, e.msg) + raise + return e def run(self): while True: try: try: self._create_build() - except socket.error, e: + except urllib2.URLError, e: log.error(e) raise ExitSlave() except ExitSlave: @@ -120,19 +134,19 @@ 'Content-Type': 'application/x-bitten+xml' }) - if resp.status == 201: - self._initiate_build(resp.getheader('location')) - elif resp.status == 204: + if resp.code == 201: + self._initiate_build(resp.info().get('location')) + elif resp.code == 204: log.info(resp.read()) else: - log.error('Unexpected response (%d): %s', resp.status, resp.reason) + log.error('Unexpected response (%d %s)', resp.code, resp.msg) raise ExitSlave() def _initiate_build(self, build_url): build_id = int(build_url.split('/')[-1]) log.info('Build %s pending at %s', build_id, build_url) resp = self.request('GET', build_url) - if resp.status == 200: + if resp.code == 200: xml = xmlio.parse(resp) basedir = os.path.join(self.work_dir, 'build_%d' % build_id) if not os.path.exists(basedir): @@ -148,7 +162,7 @@ log.info('Exiting after single build completed.') raise ExitSlave() else: - log.error('Unexpected response (%d): %s', resp.status, resp.reason) + log.error('Unexpected response (%d): %s', resp.code, resp.msg) raise ExitSlave() def _execute_build(self, build_url, recipe): @@ -190,8 +204,8 @@ resp = self.request('POST', build_url + '/steps/', str(xml), { 'Content-Type': 'application/x-bitten+xml' }) - if resp.status != 201: - log.error('Unexpected response (%d): %s', resp.status, resp.reason) + if resp.code != 201: + log.error('Unexpected response (%d): %s', resp.code, resp.msg) return not failed or step.onerror != 'fail' @@ -230,6 +244,10 @@ parser.add_option('-s', '--single', action='store_true', dest='single_build', help='exit after completing a single build') + parser.add_option('-u', '--user', dest='username', + help='the username to use for authentication') + parser.add_option('-p', '--password', dest='password', + help='the password to use when authenticating') parser.set_defaults(dry_run=False, keep_files=False, loglevel=logging.WARNING, single_build=False) options, args = parser.parse_args() @@ -256,7 +274,8 @@ slave = BuildSlave(url, name=options.name, config=options.config, dry_run=options.dry_run, work_dir=options.work_dir, keep_files=options.keep_files, - single_build=options.single_build) + single_build=options.single_build, + username=options.username, password=options.password) try: try: slave.run()