mirror of
https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
synced 2025-02-11 06:27:03 +00:00
96df4080b9
Add new autopkgtest policy: it determines the autopkgtests for a source package (its own, direct reverse binary dependencies, and Testsuite-Triggers), requests tests via AMQP, fetches results from swift, and keeps track of pending tests between run. This also caches the downloaded results from swift, as re-dowloading them all is very expensive. This introduces two new hints: * force-badtest pkg/ver[/arch]: Failing results for that package will be ignored. This is useful to deal with broken tests that get imported from Debian or are from under-maintained packages, or broke due to some infrastructure changes. These are long-lived usually. * force-skiptest pkg/ver: Test results *triggered by* that package (i. e. reverse dependencies) will be ignored. This is mostly useful for landing packages that trigger a huge amount of tests (glibc, perl) where some tests are just too flaky to get them all passing, and one just wants to land it after the remaining failures have been checked. This should be used rarely and the hints should be removed immediately again. Add integration tests that call britney in various scenarios on constructed fake archives, with mocked AMQP and Swift results.
171 lines
5.8 KiB
Python
171 lines
5.8 KiB
Python
# Mock a Swift server with autopkgtest results
|
|
# Author: Martin Pitt <martin.pitt@ubuntu.com>
|
|
|
|
import os
|
|
import tarfile
|
|
import io
|
|
import sys
|
|
import socket
|
|
import time
|
|
import tempfile
|
|
import json
|
|
|
|
try:
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
from urllib.parse import urlparse, parse_qs
|
|
except ImportError:
|
|
# Python 2
|
|
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
|
from urlparse import urlparse, parse_qs
|
|
|
|
|
|
class SwiftHTTPRequestHandler(BaseHTTPRequestHandler):
|
|
'''Mock swift container with autopkgtest results
|
|
|
|
This accepts retrieving a particular result.tar (e. g.
|
|
/container/path/result.tar) or listing the container contents
|
|
(/container/?prefix=foo&delimiter=@&marker=foo/bar).
|
|
'''
|
|
# map container -> result.tar path -> (exitcode, testpkg-version[, testinfo])
|
|
results = {}
|
|
|
|
def do_GET(self):
|
|
p = urlparse(self.path)
|
|
path_comp = p.path.split('/')
|
|
container = path_comp[1]
|
|
path = '/'.join(path_comp[2:])
|
|
if path:
|
|
self.serve_file(container, path)
|
|
else:
|
|
self.list_container(container, parse_qs(p.query))
|
|
|
|
def serve_file(self, container, path):
|
|
if os.path.basename(path) != 'result.tar':
|
|
self.send_error(404, 'File not found (only result.tar supported)')
|
|
return
|
|
try:
|
|
fields = self.results[container][os.path.dirname(path)]
|
|
try:
|
|
(exitcode, pkgver, testinfo) = fields
|
|
except ValueError:
|
|
(exitcode, pkgver) = fields
|
|
testinfo = None
|
|
except KeyError:
|
|
self.send_error(404, 'File not found')
|
|
return
|
|
|
|
self.send_response(200)
|
|
self.send_header('Content-type', 'application/octet-stream')
|
|
self.end_headers()
|
|
|
|
tar = io.BytesIO()
|
|
with tarfile.open('result.tar', 'w', tar) as results:
|
|
# add exitcode
|
|
contents = ('%i' % exitcode).encode()
|
|
ti = tarfile.TarInfo('exitcode')
|
|
ti.size = len(contents)
|
|
results.addfile(ti, io.BytesIO(contents))
|
|
# add testpkg-version
|
|
if pkgver is not None:
|
|
contents = pkgver.encode()
|
|
ti = tarfile.TarInfo('testpkg-version')
|
|
ti.size = len(contents)
|
|
results.addfile(ti, io.BytesIO(contents))
|
|
# add testinfo.json
|
|
if testinfo:
|
|
contents = json.dumps(testinfo).encode()
|
|
ti = tarfile.TarInfo('testinfo.json')
|
|
ti.size = len(contents)
|
|
results.addfile(ti, io.BytesIO(contents))
|
|
|
|
self.wfile.write(tar.getvalue())
|
|
|
|
def list_container(self, container, query):
|
|
try:
|
|
objs = set(['%s/result.tar' % r for r in self.results[container]])
|
|
except KeyError:
|
|
self.send_error(401, 'Container does not exist')
|
|
return
|
|
if 'prefix' in query:
|
|
p = query['prefix'][-1]
|
|
objs = set([o for o in objs if o.startswith(p)])
|
|
if 'delimiter' in query:
|
|
d = query['delimiter'][-1]
|
|
# if find() returns a value, we want to include the delimiter, thus
|
|
# bump its result; for "not found" return None
|
|
find_adapter = lambda i: (i >= 0) and (i + 1) or None
|
|
objs = set([o[:find_adapter(o.find(d))] for o in objs])
|
|
if 'marker' in query:
|
|
m = query['marker'][-1]
|
|
objs = set([o for o in objs if o > m])
|
|
|
|
self.send_response(objs and 200 or 204) # 204: "No Content"
|
|
self.send_header('Content-type', 'text/plain')
|
|
self.end_headers()
|
|
self.wfile.write(('\n'.join(sorted(objs)) + '\n').encode('UTF-8'))
|
|
|
|
|
|
class AutoPkgTestSwiftServer:
|
|
def __init__(self, port=8080):
|
|
self.port = port
|
|
self.server_pid = None
|
|
self.log = None
|
|
|
|
def __del__(self):
|
|
if self.server_pid:
|
|
self.stop()
|
|
|
|
@classmethod
|
|
def set_results(klass, results):
|
|
'''Set served results.
|
|
|
|
results is a map: container -> result.tar path ->
|
|
(exitcode, testpkg-version, testinfo)
|
|
'''
|
|
SwiftHTTPRequestHandler.results = results
|
|
|
|
def start(self):
|
|
assert self.server_pid is None, 'already started'
|
|
if self.log:
|
|
self.log.close()
|
|
self.log = tempfile.TemporaryFile()
|
|
p = os.fork()
|
|
if p:
|
|
# parent: wait until server starts
|
|
self.server_pid = p
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
while True:
|
|
if s.connect_ex(('127.0.0.1', self.port)) == 0:
|
|
break
|
|
time.sleep(0.1)
|
|
s.close()
|
|
return
|
|
|
|
# child; quiesce logging on stderr
|
|
os.dup2(self.log.fileno(), sys.stderr.fileno())
|
|
srv = HTTPServer(('', self.port), SwiftHTTPRequestHandler)
|
|
srv.serve_forever()
|
|
sys.exit(0)
|
|
|
|
def stop(self):
|
|
assert self.server_pid, 'not running'
|
|
os.kill(self.server_pid, 15)
|
|
os.waitpid(self.server_pid, 0)
|
|
self.server_pid = None
|
|
self.log.close()
|
|
|
|
if __name__ == '__main__':
|
|
srv = AutoPkgTestSwiftServer()
|
|
srv.set_results({'autopkgtest-series': {
|
|
'series/i386/d/darkgreen/20150101_100000@': (0, 'darkgreen 1'),
|
|
'series/i386/g/green/20150101_100000@': (0, 'green 1', {'custom_environment': ['ADT_TEST_TRIGGERS=green']}),
|
|
'series/i386/l/lightgreen/20150101_100000@': (0, 'lightgreen 1'),
|
|
'series/i386/l/lightgreen/20150101_100101@': (4, 'lightgreen 2'),
|
|
'series/i386/l/lightgreen/20150101_100102@': (0, 'lightgreen 3'),
|
|
}})
|
|
srv.start()
|
|
print('Running on http://localhost:8080/autopkgtest-series')
|
|
print('Press Enter to quit.')
|
|
sys.stdin.readline()
|
|
srv.stop()
|