mirror of
https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
synced 2025-06-08 08:11:33 +00:00
Add a mock swiftclient for testing, add unit tests for private PPAs and private non-PPA runs, fix some issues discovered through the testing.
This commit is contained in:
parent
2de94184b3
commit
e2932055a9
@ -906,20 +906,18 @@ class AutopkgtestPolicy(BasePolicy):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
_, result_paths = self.swift_conn.get_container(
|
_, result_paths = self.swift_conn.get_container(
|
||||||
self.swift_conn.url,
|
self.swift_container,
|
||||||
token=self.swift_conn.token,
|
|
||||||
container=self.swift_container,
|
|
||||||
query_string=urllib.parse.urlencode(query))
|
query_string=urllib.parse.urlencode(query))
|
||||||
except ClientException as e:
|
except ClientException as e:
|
||||||
# 401 "Unauthorized" is swift's way of saying "container does not exist"
|
# 401 "Unauthorized" is swift's way of saying "container does not exist"
|
||||||
if e.http_code == 401 or e.http_code == 404:
|
if e.http_status == '401' or e.http_status == '404':
|
||||||
self.logger.info('fetch_swift_results: %s does not exist yet or is inaccessible', url)
|
self.logger.info('fetch_swift_results: %s does not exist yet or is inaccessible', self.swift_container)
|
||||||
return
|
return
|
||||||
# Other status codes are usually a transient
|
# Other status codes are usually a transient
|
||||||
# network/infrastructure failure. Ignoring this can lead to
|
# network/infrastructure failure. Ignoring this can lead to
|
||||||
# re-requesting tests which we already have results for, so
|
# re-requesting tests which we already have results for, so
|
||||||
# fail hard on this and let the next run retry.
|
# fail hard on this and let the next run retry.
|
||||||
self.logger.error('Failure to fetch swift results from %s: %s', url, str(e))
|
self.logger.error('Failure to fetch swift results from %s: %s', self.swift_container, str(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
url = os.path.join(swift_url, self.swift_container)
|
url = os.path.join(swift_url, self.swift_container)
|
||||||
@ -968,13 +966,13 @@ class AutopkgtestPolicy(BasePolicy):
|
|||||||
|
|
||||||
# We don't need any additional retry logic as swiftclient
|
# We don't need any additional retry logic as swiftclient
|
||||||
# already performs retries (5 by default).
|
# already performs retries (5 by default).
|
||||||
full_path = os.path.join(path, name)
|
url = os.path.join(path, name)
|
||||||
try:
|
try:
|
||||||
_, contents = swift_conn.get_object(container, full_path)
|
_, contents = self.swift_conn.get_object(container, url)
|
||||||
except ClientException as e:
|
except ClientException as e:
|
||||||
self.logger.error('Failure to fetch %s from container %s: %s',
|
self.logger.error('Failure to fetch %s from container %s: %s',
|
||||||
full_path, container, str(e))
|
url, container, str(e))
|
||||||
if e.http_code == 404:
|
if e.http_status == '404':
|
||||||
return
|
return
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
tar_bytes = io.BytesIO(contents)
|
tar_bytes = io.BytesIO(contents)
|
||||||
@ -1114,7 +1112,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|||||||
params['submit-time'] = datetime.strftime(datetime.utcnow(), '%Y-%m-%d %H:%M:%S%z')
|
params['submit-time'] = datetime.strftime(datetime.utcnow(), '%Y-%m-%d %H:%M:%S%z')
|
||||||
|
|
||||||
if self.swift_conn:
|
if self.swift_conn:
|
||||||
params['readable-by'] = self.adt_swift_user
|
params['readable-by'] = self.options.adt_swift_user
|
||||||
|
|
||||||
if self.amqp_channel:
|
if self.amqp_channel:
|
||||||
import amqplib.client_0_8 as amqp
|
import amqplib.client_0_8 as amqp
|
||||||
|
@ -19,6 +19,10 @@ except ImportError:
|
|||||||
from urlparse import urlparse, parse_qs
|
from urlparse import urlparse, parse_qs
|
||||||
|
|
||||||
|
|
||||||
|
TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PROJECT_DIR = os.path.dirname(TESTS_DIR)
|
||||||
|
|
||||||
|
|
||||||
class SwiftHTTPRequestHandler(BaseHTTPRequestHandler):
|
class SwiftHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||||
'''Mock swift container with autopkgtest results
|
'''Mock swift container with autopkgtest results
|
||||||
|
|
||||||
@ -124,7 +128,15 @@ class AutoPkgTestSwiftServer:
|
|||||||
'''
|
'''
|
||||||
SwiftHTTPRequestHandler.results = results
|
SwiftHTTPRequestHandler.results = results
|
||||||
|
|
||||||
def start(self):
|
def start(self, swiftclient=False):
|
||||||
|
if swiftclient:
|
||||||
|
# since we're running britney directly, the only way to reliably
|
||||||
|
# mock out the swiftclient module is to override it in the local
|
||||||
|
# path with the dummy version we created
|
||||||
|
src = os.path.join(TESTS_DIR, 'mock_swiftclient.py')
|
||||||
|
dst = os.path.join(PROJECT_DIR, 'swiftclient.py')
|
||||||
|
os.symlink(src, dst)
|
||||||
|
|
||||||
assert self.server_pid is None, 'already started'
|
assert self.server_pid is None, 'already started'
|
||||||
if self.log:
|
if self.log:
|
||||||
self.log.close()
|
self.log.close()
|
||||||
@ -148,12 +160,19 @@ class AutoPkgTestSwiftServer:
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
# in case we were 'mocking out' swiftclient, remove the symlink we
|
||||||
|
# created earlier during start()
|
||||||
|
swiftclient_mod = os.path.join(PROJECT_DIR, 'swiftclient.py')
|
||||||
|
if os.path.islink(swiftclient_mod):
|
||||||
|
os.unlink(swiftclient_mod)
|
||||||
|
|
||||||
assert self.server_pid, 'not running'
|
assert self.server_pid, 'not running'
|
||||||
os.kill(self.server_pid, 15)
|
os.kill(self.server_pid, 15)
|
||||||
os.waitpid(self.server_pid, 0)
|
os.waitpid(self.server_pid, 0)
|
||||||
self.server_pid = None
|
self.server_pid = None
|
||||||
self.log.close()
|
self.log.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
srv = AutoPkgTestSwiftServer()
|
srv = AutoPkgTestSwiftServer()
|
||||||
srv.set_results({'autopkgtest-testing': {
|
srv.set_results({'autopkgtest-testing': {
|
||||||
|
70
tests/mock_swiftclient.py
Normal file
70
tests/mock_swiftclient.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Mock the swiftclient Python library, the bare minimum for ADT purposes
|
||||||
|
# Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
|
||||||
|
# We want to use this single Python module file to mock out the exception
|
||||||
|
# module as well.
|
||||||
|
sys.modules["swiftclient.exceptions"] = sys.modules[__name__]
|
||||||
|
|
||||||
|
|
||||||
|
class ClientException(Exception):
|
||||||
|
def __init__(self, msg, http_status=''):
|
||||||
|
super(ClientException, self).__init__(msg)
|
||||||
|
self.msg = msg
|
||||||
|
self.http_status = http_status
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self, authurl, user, key, tenant_name, auth_version):
|
||||||
|
self._mocked_swift = 'http://localhost:18085'
|
||||||
|
|
||||||
|
def get_container(self, container, marker=None, limit=None, prefix=None,
|
||||||
|
delimiter=None, end_marker=None, path=None,
|
||||||
|
full_listing=False, headers=None, query_string=None):
|
||||||
|
url = os.path.join(self._mocked_swift, container) + '?' + query_string
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
|
req = urlopen(url, timeout=30)
|
||||||
|
code = req.getcode()
|
||||||
|
if code == 200:
|
||||||
|
result_paths = req.read().decode().strip().splitlines()
|
||||||
|
elif code == 204: # No content
|
||||||
|
result_paths = []
|
||||||
|
else:
|
||||||
|
raise ClientException('MockedError', http_status=str(code))
|
||||||
|
except IOError as e:
|
||||||
|
# 401 "Unauthorized" is swift's way of saying "container does not exist"
|
||||||
|
# But here we just assume swiftclient handles this via the usual
|
||||||
|
# ClientException.
|
||||||
|
raise ClientException('MockedError', http_status=str(e.code) if hasattr(e, 'code') else '')
|
||||||
|
finally:
|
||||||
|
if req is not None:
|
||||||
|
req.close()
|
||||||
|
|
||||||
|
return (None, result_paths)
|
||||||
|
|
||||||
|
def get_object(self, container, obj):
|
||||||
|
url = os.path.join(self._mocked_swift, container, obj)
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
|
req = urlopen(url, timeout=30)
|
||||||
|
code = req.getcode()
|
||||||
|
if code == 200:
|
||||||
|
contents = req.read()
|
||||||
|
else:
|
||||||
|
raise ClientException('MockedError', http_status=str(code))
|
||||||
|
except IOError as e:
|
||||||
|
# 401 "Unauthorized" is swift's way of saying "container does not exist"
|
||||||
|
# But here we just assume swiftclient handles this via the usual
|
||||||
|
# ClientException.
|
||||||
|
raise ClientException('MockedError', http_status=str(e.code) if hasattr(e, 'code') else '')
|
||||||
|
finally:
|
||||||
|
if req is not None:
|
||||||
|
req.close()
|
||||||
|
|
||||||
|
return (None, contents)
|
@ -16,6 +16,7 @@ import urllib.parse
|
|||||||
|
|
||||||
import apt_pkg
|
import apt_pkg
|
||||||
import yaml
|
import yaml
|
||||||
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
sys.path.insert(0, PROJECT_DIR)
|
sys.path.insert(0, PROJECT_DIR)
|
||||||
@ -116,7 +117,7 @@ class TestAutopkgtestBase(TestBase):
|
|||||||
with open(email_path, 'w', encoding='utf-8') as email:
|
with open(email_path, 'w', encoding='utf-8') as email:
|
||||||
email.write(json.dumps(self.email_cache))
|
email.write(json.dumps(self.email_cache))
|
||||||
|
|
||||||
self.swift.start()
|
self.swift.start(swiftclient=True)
|
||||||
(excuses_yaml, excuses_html, out) = self.run_britney()
|
(excuses_yaml, excuses_html, out) = self.run_britney()
|
||||||
self.swift.stop()
|
self.swift.stop()
|
||||||
|
|
||||||
@ -2552,7 +2553,15 @@ class AT(TestAutopkgtestBase):
|
|||||||
|
|
||||||
for line in fileinput.input(self.britney_conf, inplace=True):
|
for line in fileinput.input(self.britney_conf, inplace=True):
|
||||||
if line.startswith('ADT_PPAS'):
|
if line.startswith('ADT_PPAS'):
|
||||||
print('ADT_PPAS = user:password@joe/foo')
|
print('ADT_PPAS = first/ppa user:password@joe/foo:DEADBEEF')
|
||||||
|
elif line.startswith('ADT_SWIFT_USER'):
|
||||||
|
print('ADT_SWIFT_USER = user')
|
||||||
|
elif line.startswith('ADT_SWIFT_PASS'):
|
||||||
|
print('ADT_SWIFT_PASS = pass')
|
||||||
|
elif line.startswith('ADT_SWIFT_TENANT'):
|
||||||
|
print('ADT_SWIFT_TENANT = tenant')
|
||||||
|
elif line.startswith('ADT_SWIFT_AUTH_URL'):
|
||||||
|
print('ADT_SWIFT_AUTH_URL = http://127.0.0.1:5000/v2.0/')
|
||||||
else:
|
else:
|
||||||
sys.stdout.write(line)
|
sys.stdout.write(line)
|
||||||
|
|
||||||
@ -2562,8 +2571,15 @@ class AT(TestAutopkgtestBase):
|
|||||||
{'lightgreen': [('old-version', '1'), ('new-version', '2')]}
|
{'lightgreen': [('old-version', '1'), ('new-version', '2')]}
|
||||||
)[1]
|
)[1]
|
||||||
|
|
||||||
|
# check if the private PPA info is propagated to the AMQP queue
|
||||||
|
self.assertEqual(len(self.amqp_requests), 2)
|
||||||
|
for request in self.amqp_requests:
|
||||||
|
self.assertIn('"triggers": ["lightgreen/2"]', request)
|
||||||
|
self.assertIn('"ppas": ["first/ppa", "user:password@joe/foo:DEADBEEF"]', request)
|
||||||
|
self.assertIn('"readable-by": "user"', request)
|
||||||
|
|
||||||
# add results to PPA specific swift container
|
# add results to PPA specific swift container
|
||||||
self.swift.set_results({'autopkgtest-testing-joe-foo': {
|
self.swift.set_results({'private-autopkgtest-testing-joe-foo': {
|
||||||
'testing/i386/l/lightgreen/20150101_100000@': (0, 'lightgreen 1', tr('passedbefore/1')),
|
'testing/i386/l/lightgreen/20150101_100000@': (0, 'lightgreen 1', tr('passedbefore/1')),
|
||||||
'testing/i386/l/lightgreen/20150101_100100@': (4, 'lightgreen 2', tr('lightgreen/2')),
|
'testing/i386/l/lightgreen/20150101_100100@': (4, 'lightgreen 2', tr('lightgreen/2')),
|
||||||
'testing/amd64/l/lightgreen/20150101_100101@': (0, 'lightgreen 2', tr('lightgreen/2')),
|
'testing/amd64/l/lightgreen/20150101_100101@': (0, 'lightgreen 2', tr('lightgreen/2')),
|
||||||
@ -2581,24 +2597,90 @@ class AT(TestAutopkgtestBase):
|
|||||||
{'lightgreen/2': {
|
{'lightgreen/2': {
|
||||||
'amd64': [
|
'amd64': [
|
||||||
'PASS',
|
'PASS',
|
||||||
'http://localhost:18085/autopkgtest-testing-joe-foo/'
|
'http://localhost:18085/private-autopkgtest-testing-joe-foo/'
|
||||||
'testing/amd64/l/lightgreen/20150101_100101@/log.gz',
|
'testing/amd64/l/lightgreen/20150101_100101@/log.gz',
|
||||||
None,
|
None,
|
||||||
'http://localhost:18085/autopkgtest-testing-joe-foo/'
|
'http://localhost:18085/private-autopkgtest-testing-joe-foo/'
|
||||||
'testing/amd64/l/lightgreen/20150101_100101@/artifacts.tar.gz',
|
'testing/amd64/l/lightgreen/20150101_100101@/artifacts.tar.gz',
|
||||||
None],
|
None],
|
||||||
'i386': [
|
'i386': [
|
||||||
'REGRESSION',
|
'REGRESSION',
|
||||||
'http://localhost:18085/autopkgtest-testing-joe-foo/'
|
'http://localhost:18085/private-autopkgtest-testing-joe-foo/'
|
||||||
'testing/i386/l/lightgreen/20150101_100100@/log.gz',
|
'testing/i386/l/lightgreen/20150101_100100@/log.gz',
|
||||||
None,
|
None,
|
||||||
'http://localhost:18085/autopkgtest-testing-joe-foo/'
|
'http://localhost:18085/private-autopkgtest-testing-joe-foo/'
|
||||||
'testing/i386/l/lightgreen/20150101_100100@/artifacts.tar.gz',
|
'testing/i386/l/lightgreen/20150101_100100@/artifacts.tar.gz',
|
||||||
None]},
|
None]},
|
||||||
'verdict': 'REJECTED_PERMANENTLY'})
|
'verdict': 'REJECTED_PERMANENTLY'})
|
||||||
self.assertEqual(self.amqp_requests, set())
|
self.assertEqual(self.amqp_requests, set())
|
||||||
self.assertEqual(self.pending_requests, {})
|
self.assertEqual(self.pending_requests, {})
|
||||||
|
|
||||||
|
def test_private_without_ppa(self):
|
||||||
|
'''Run a private test (without using a private PPA)'''
|
||||||
|
|
||||||
|
self.data.add_default_packages(lightgreen=False)
|
||||||
|
|
||||||
|
for line in fileinput.input(self.britney_conf, inplace=True):
|
||||||
|
if line.startswith('ADT_SWIFT_USER'):
|
||||||
|
print('ADT_SWIFT_USER = user')
|
||||||
|
elif line.startswith('ADT_SWIFT_PASS'):
|
||||||
|
print('ADT_SWIFT_PASS = pass')
|
||||||
|
elif line.startswith('ADT_SWIFT_TENANT'):
|
||||||
|
print('ADT_SWIFT_TENANT = tenant')
|
||||||
|
elif line.startswith('ADT_SWIFT_AUTH_URL'):
|
||||||
|
print('ADT_SWIFT_AUTH_URL = http://127.0.0.1:5000/v2.0/')
|
||||||
|
else:
|
||||||
|
sys.stdout.write(line)
|
||||||
|
|
||||||
|
exc = self.run_it(
|
||||||
|
[('lightgreen', {'Version': '2'}, 'autopkgtest')],
|
||||||
|
{'lightgreen': (True, {'lightgreen': {'amd64': 'RUNNING-ALWAYSFAIL'}})},
|
||||||
|
{'lightgreen': [('old-version', '1'), ('new-version', '2')]}
|
||||||
|
)[1]
|
||||||
|
|
||||||
|
# check if the user info is propagated to the AMQP queue
|
||||||
|
self.assertEqual(len(self.amqp_requests), 2)
|
||||||
|
for request in self.amqp_requests:
|
||||||
|
self.assertIn('"triggers": ["lightgreen/2"]', request)
|
||||||
|
self.assertIn('"readable-by": "user"', request)
|
||||||
|
|
||||||
|
# add results to PPA specific swift container
|
||||||
|
self.swift.set_results({'private-autopkgtest-testing': {
|
||||||
|
'testing/i386/l/lightgreen/20150101_100000@': (0, 'lightgreen 1', tr('passedbefore/1')),
|
||||||
|
'testing/i386/l/lightgreen/20150101_100100@': (4, 'lightgreen 2', tr('lightgreen/2')),
|
||||||
|
'testing/amd64/l/lightgreen/20150101_100101@': (0, 'lightgreen 2', tr('lightgreen/2')),
|
||||||
|
}})
|
||||||
|
|
||||||
|
exc = self.run_it(
|
||||||
|
[],
|
||||||
|
{'lightgreen': (False, {'lightgreen/2': {'i386': 'REGRESSION', 'amd64': 'PASS'}})},
|
||||||
|
{'lightgreen': [('old-version', '1'), ('new-version', '2')]}
|
||||||
|
)[1]
|
||||||
|
|
||||||
|
# check if the right container name is used and that we still have the
|
||||||
|
# retry url (it should only be hidden for private PPAs)
|
||||||
|
self.assertEqual(
|
||||||
|
exc['lightgreen']['policy_info']['autopkgtest'],
|
||||||
|
{'lightgreen/2': {
|
||||||
|
'amd64': [
|
||||||
|
'PASS',
|
||||||
|
'http://localhost:18085/private-autopkgtest-testing/'
|
||||||
|
'testing/amd64/l/lightgreen/20150101_100101@/log.gz',
|
||||||
|
'https://autopkgtest.ubuntu.com/packages/l/lightgreen/testing/amd64',
|
||||||
|
None,
|
||||||
|
None],
|
||||||
|
'i386': [
|
||||||
|
'REGRESSION',
|
||||||
|
'http://localhost:18085/private-autopkgtest-testing/'
|
||||||
|
'testing/i386/l/lightgreen/20150101_100100@/log.gz',
|
||||||
|
'https://autopkgtest.ubuntu.com/packages/l/lightgreen/testing/i386',
|
||||||
|
None,
|
||||||
|
'https://autopkgtest.ubuntu.com/request.cgi?release=testing&arch=i386&package=lightgreen&'
|
||||||
|
'trigger=lightgreen%2F2']},
|
||||||
|
'verdict': 'REJECTED_PERMANENTLY'})
|
||||||
|
self.assertEqual(self.amqp_requests, set())
|
||||||
|
self.assertEqual(self.pending_requests, {})
|
||||||
|
|
||||||
def test_disable_upgrade_tester(self):
|
def test_disable_upgrade_tester(self):
|
||||||
'''Run without second stage upgrade tester'''
|
'''Run without second stage upgrade tester'''
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user