[Reproducible-commits] [reprotest] 06/06: First draft of integration of the virtualization
Ceridwen
ceridwen-guest at moszumanska.debian.org
Fri Jun 24 18:46:20 UTC 2016
This is an automated email from the git hooks/post-receive script.
ceridwen-guest pushed a commit to branch virtualization
in repository reprotest.
commit a7c798e80501c89b3c155582042d1b21287f91f4
Author: Ceridwen <ceridwenv at gmail.com>
Date: Fri Jun 24 14:41:12 2016 -0400
First draft of integration of the virtualization
---
reprotest/__init__.py | 215 +++++++++++++++++++++++++------------------
reprotest/lib/adt_testbed.py | 4 +-
tests/tests.py | 4 +-
tox.ini | 2 +-
4 files changed, 129 insertions(+), 96 deletions(-)
diff --git a/reprotest/__init__.py b/reprotest/__init__.py
index b77fcf2..073dbdf 100644
--- a/reprotest/__init__.py
+++ b/reprotest/__init__.py
@@ -18,8 +18,12 @@ import types
import pkg_resources
+from reprotest.lib import adtlog
from reprotest.lib import adt_testbed
+# adtlog.verbosity = 2
+
+
# time zone, locales, disorderfs, host name, user/group, shell, CPU
# number, architecture for uname (using linux64), umask, HOME, see
# also: https://tests.reproducible-builds.org/index_variations.html
@@ -31,60 +35,74 @@ from reprotest.lib import adt_testbed
# approaches.
@contextlib.contextmanager
-def virt_server_manager(server_type, args):
+def virtual_server(args, temp_dir):
'''This is a simple wrapper around adt_testbed that automates the
clean up.'''
# Find the location of reprotest using setuptools and then get the
# path for the correct virt-server script.
server_path = pkg_resources.resource_filename(__name__, 'virt/' +
- server_type)
- virt_server = adt_testbed.Testbed((server_path,) + args, '/tmp/outdir', None)
- virt_server.start()
- virt_server.open()
+ args[0])
+ virtual_server = adt_testbed.Testbed([server_path] + args[1:], temp_dir, None)
+ virtual_server.start()
+ virtual_server.open()
try:
- yield virt_server
+ yield virtual_server
finally:
- virt_server.stop()
+ virtual_server.stop()
+
+
+Pair = collections.namedtuple('Pair', 'control experiment')
+
+def add(mapping, key, value):
+ new_mapping = mapping.copy()
+ new_mapping[key] = value
+ return types.MappingProxyType(new_mapping)
# TODO: relies on a pbuilder-specific command to parallelize
-# def cpu(command1, command2, env1, env2, tree1, tree2):
-# yield command1, command2, env1, env2, tree1, tree2
+# @contextlib.contextmanager
+# def cpu(env, tree, builder):
+# yield command, env, tree
@contextlib.contextmanager
-def captures_environment(command1, command2, env1, env2, tree1, tree2):
- env2['CAPTURE_ENVIRONMENT'] = 'i_capture_the_environment'
- yield command1, command2, env1, env2, tree1, tree2
+def captures_environment(command, env, tree, builder):
+ new_env = add(env.experiment, 'CAPTURE_ENVIRONMENT',
+ 'i_capture_the_environment')
+ yield command, Pair(env.control, new_env), tree
# TODO: this requires superuser privileges.
@contextlib.contextmanager
-def domain_host(command1, command2, env1, env2, tree1, tree2):
- yield command1, command2, env1, env2, tree1, tree2
+def domain_host(command, env, tree, builder):
+ yield command, env, tree
@contextlib.contextmanager
-def fileordering(command1, command2, env1, env2, tree1, tree2):
- disorderfs = tree2.parent/'disorderfs'
- disorderfs.mkdir()
- subprocess.check_call(['disorderfs', '--shuffle-dirents=yes',
- str(tree2), str(disorderfs)])
+def fileordering(command, env, tree, builder):
+ new_tree = os.path.dirname(os.path.dirname(tree.control)) + '/disorderfs/'
+ builder.execute(['mkdir', '-p', new_tree])
+ # disorderfs = tree2.parent/'disorderfs'
+ # disorderfs.mkdir()
+ builder.execute(['disorderfs', '--shuffle-dirents=yes',
+ tree.experiment, new_tree])
try:
- yield command1, command2, env1, env2, tree1, disorderfs
+ yield command, env, Pair(tree.control, new_tree)
finally:
- subprocess.check_call(['fusermount', '-u', str(disorderfs)])
+ # subprocess.check_call(['fusermount', '-u', str(disorderfs)])
+ builder.execute(['fusermount', '-u', new_tree])
@contextlib.contextmanager
-def home(command1, command2, env1, env2, tree1, tree2):
- env1['HOME'] = '/nonexistent/first-build'
- env2['HOME'] = '/nonexistent/second-build'
- yield command1, command2, env1, env2, tree1, tree2
+def home(command, env, tree, builder):
+ control = add(env.experiment, 'HOME', '/nonexistent/first-build')
+ experiment = add(env.experiment, 'HOME', '/nonexistent/second-build')
+ yield command, Pair(control, experiment), tree
# TODO: uname is a POSIX standard. The related Linux command
# (setarch) only affects uname at the moment according to the docs.
# FreeBSD changes uname with environment variables. Wikipedia has a
-# reference to a setname command: https://en.wikipedia.org/wiki/Uname
+# reference to a setname command on another Unix variant:
+# https://en.wikipedia.org/wiki/Uname
@contextlib.contextmanager
-def kernel(command1, command2, env1, env2, tree1, tree2):
- command2 = ['linux64', '--uname-2.6'] + command2
- yield command1, command2, env1, env2, tree1, tree2
+def kernel(command, env, tree, builder):
+ new_command = ['linux64', '--uname-2.6'] + command.experiment
+ yield Pair(command.control, new_command), env, tree
# TODO: if this locale doesn't exist on the system, Python's
# locales.getlocale() will return (None, None) rather than this
@@ -99,50 +117,52 @@ def kernel(command1, command2, env1, env2, tree1, tree2):
# TODO: what exact locales and how to many test is probably a mailing
# list question.
@contextlib.contextmanager
-def locales(command1, command2, env1, env2, tree1, tree2):
+def locales(command, env, tree, builder):
# env1['LANG'] = 'C'
- env2['LANG'] = 'fr_CH.UTF-8'
+ new_env = add(add(env.experiment, 'LANG', 'fr_CH.UTF-8'),
+ 'LC_ALL', 'fr_CH.UTF-8')
# env1['LANGUAGE'] = 'en_US:en'
# env2['LANGUAGE'] = 'fr_CH:fr'
- env2['LC_ALL'] = 'fr_CH.UTF-8'
- yield command1, command2, env1, env2, tree1, tree2
+ yield command, Pair(env.control, new_env), tree
# TODO: Linux-specific. unshare --uts requires superuser privileges.
# How is this related to host/domainname?
-# def namespace(command1, command2, env1, env2, tree1, tree2):
+# def namespace(command, env, tree, builder):
# # command1 = ['unshare', '--uts'] + command1
# # command2 = ['unshare', '--uts'] + command2
-# yield command1, command2, env1, env2, tree1, tree2
+# yield command, env, tree
@contextlib.contextmanager
-def path(command1, command2, env1, env2, tree1, tree2):
- env2['PATH'] = env1['PATH'] + '/i_capture_the_path'
- yield command1, command2, env1, env2, tree1, tree2
+def path(command, env, tree, builder):
+ new_env = add(env.experiment, 'PATH', env.control['PATH'] +
+ '/i_capture_the_path')
+ yield command, Pair(env.control, new_env), tree
# This doesn't require superuser privileges, but the chsh command
# affects all user shells, which would be bad.
@contextlib.contextmanager
-def shell(command1, command2, env1, env2, tree1, tree2):
- yield command1, command2, env1, env2, tree1, tree2
+def shell(command, env, tree, builder):
+ yield command, env, tree
@contextlib.contextmanager
-def timezone(command1, command2, env1, env2, tree1, tree2):
+def timezone(command, env, tree, builder):
# These time zones are theoretically in the POSIX time zone format
# (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08),
# so they should be cross-platform compatible.
- env1['TZ'] = 'GMT+12'
- env2['TZ'] = 'GMT-14'
- yield command1, command2, env1, env2, tree1, tree2
+ control = add(env.experiment, 'TZ', 'GMT+12')
+ experiment = add(env.experiment, 'TZ', 'GMT-14')
+ yield command, Pair(control, experiment), tree
- at contextlib.contextmanager
-def umask(command1, command2, env1, env2, tree1, tree2):
- command2 = ['umask', '0002;'] + command2
- yield command1, command2, env1, env2, tree1, tree2
+# TODO: figure out how to make this compatible with adt_testbed.
+
+# def umask(command, env, tree, builder):
+# command2 = ['umask', '0002;'] + command2
+# yield command, env, tree
# TODO: This requires superuser privileges.
@contextlib.contextmanager
-def user_group(command1, command2, env1, env2, tree1, tree2):
- yield command1, command2, env1, env2, tree1, tree2
+def user_group(command, env, tree, builder):
+ yield command, env, tree
VARIATIONS = types.MappingProxyType(collections.OrderedDict([
('captures_environment', captures_environment),
@@ -151,54 +171,66 @@ VARIATIONS = types.MappingProxyType(collections.OrderedDict([
('home', home), ('kernel', kernel), ('locales', locales),
# 'namespace': namespace,
('path', path), ('shell', shell),
- ('timezone', timezone), ('umask', umask), ('user_group', user_group)
+ ('timezone', timezone), # ('umask', umask)
+ ('user_group', user_group)
]))
-def build(command, source_root, built_artifact, artifact_store, **kws):
+def build(command, source_root, built_artifact, builder, artifact_store, env):
# print(command)
# print(source_root)
# print(list(pathlib.Path(source_root).glob('*')))
# print(kws)
# print(subprocess.check_output(['ls'], cwd=source_root, **kws).decode('ascii'))
# print(subprocess.check_output('python --version', cwd=source_root, **kws))
- subprocess.check_call(command, cwd=source_root, **kws)
- with open(built_artifact, 'rb') as artifact:
- artifact_store.write(artifact.read())
- artifact_store.flush()
-
-def check(build_command, artifact_name, virt_server_args, source_root,
+ builder.execute(command, xenv=[str(k) + '=' + str(v) for k, v in env.items()], cwd=source_root)
+ # subprocess.check_call(command, cwd=source_root, **kws)
+ # with open(built_artifact, 'rb') as artifact:
+ # artifact_store.write(artifact.read())
+ # artifact_store.flush()
+ builder.command('copyup', (built_artifact, artifact_store))
+
+def check(build_command, artifact_name, virtual_server_args, source_root,
variations=VARIATIONS):
- # print(virt_server_args)
- with tempfile.TemporaryDirectory() as temp:
- command1 = build_command
- command2 = build_command
- env1 = os.environ.copy()
- env2 = env1.copy()
- tree1 = pathlib.Path(shutil.copytree(str(source_root), temp + '/tree1'))
- tree2 = pathlib.Path(shutil.copytree(str(source_root), temp + '/tree2'))
+ # print(virtual_server_args)
+ with tempfile.TemporaryDirectory() as temp_dir, virtual_server(virtual_server_args, temp_dir) as builder:
+ command = Pair(build_command, build_command)
+ env = Pair(types.MappingProxyType(os.environ.copy()),
+ types.MappingProxyType(os.environ.copy()))
+ # TODO, why?: directories need explicit '/' appended for VirtSubproc
+ tree = Pair(temp_dir + '/control/', temp_dir + '/experiment/')
+ builder.command('copydown', (str(source_root) + '/', tree.control))
+ builder.command('copydown', (str(source_root) + '/', tree.experiment))
+ # tree1 = pathlib.Path(shutil.copytree(str(source_root), temp_dir + '/tree1'))
+ # tree2 = pathlib.Path(shutil.copytree(str(source_root), temp_dir + '/tree2'))
# print(' '.join(command1))
# print(pathlib.Path.cwd())
# print(source_root)
try:
with contextlib.ExitStack() as stack:
- for variation in variations:
- # print(variation)
- command1, command2, env1, env2, tree1, tree2 = stack.enter_context(VARIATIONS[variation](command1, command2, env1, env2, tree1, tree2))
- # I would prefer to use pathlib here but
- # .resolve(), to eliminate ../ references, doesn't
- # work on nonexistent paths.
- build(' '.join(command1), str(tree1),
- os.path.normpath(temp + '/tree1/' + artifact_name),
- open(os.path.normpath(temp + '/artifact1'), 'wb'),
- env=env1, shell=True)
- build(' '.join(command2), str(tree2),
- os.path.normpath(temp + '/tree2/' + artifact_name),
- open(os.path.normpath(temp + '/artifact2'), 'wb'),
- env=env2, shell=True)
+ for variation in variations:
+ # print('START')
+ # print(variation)
+ command, env, tree = stack.enter_context(VARIATIONS[variation](command, env, tree, builder))
+ # print(command)
+ # print(env)
+ # print(tree)
+ # I would prefer to use pathlib here but
+ # .resolve(), to eliminate ../ references, doesn't
+ # work on nonexistent paths.
+ build(command.control, tree.control,
+ os.path.normpath(tree.control + artifact_name),
+ builder,
+ os.path.normpath(temp_dir + '/control_artifact'),
+ env=env.control)
+ build(command.experiment, tree.experiment,
+ os.path.normpath(tree.experiment + artifact_name),
+ builder,
+ os.path.normpath(temp_dir + '/experiment_artifact'),
+ env=env.experiment)
except Exception:
# traceback.print_exc()
sys.exit(2)
- sys.exit(subprocess.call(['diffoscope', temp + '/artifact1', temp + '/artifact2']))
+ sys.exit(subprocess.call(['diffoscope', temp_dir + '/control_artifact', temp_dir + '/experiment_artifact']))
COMMAND_LINE_OPTIONS = types.MappingProxyType(collections.OrderedDict([
@@ -208,9 +240,9 @@ COMMAND_LINE_OPTIONS = types.MappingProxyType(collections.OrderedDict([
('artifact', types.MappingProxyType({
'default': None, 'nargs': '?',
'help': 'Build artifact to test for reproducibility.'})),
- ('virt_server_args', types.MappingProxyType({
+ ('virtual_server_args', types.MappingProxyType({
'default': None, 'nargs': '*',
- 'help': 'Arguments to pass to the virt_server.'})),
+ 'help': 'Arguments to pass to the virtual_server.'})),
('--source-root', types.MappingProxyType({
'dest': 'source_root', 'type': pathlib.Path,
'help': 'Root of the source tree, if not the '
@@ -231,7 +263,7 @@ COMMAND_LINE_OPTIONS = types.MappingProxyType(collections.OrderedDict([
]))
MULTIPLET_OPTIONS = frozenset(['build_command', 'dont_vary',
- 'variations', 'virt_server_args'])
+ 'variations', 'virtual_server_args'])
CONFIG_OPTIONS = []
for option in COMMAND_LINE_OPTIONS.keys():
@@ -280,9 +312,9 @@ def main():
artifact = command_line_options.get(
'artifact',
config_options.get('artifact'))
- virt_server_args = command_line_options.get(
- 'virt_server_args',
- config_options.get('virt_server_args'))
+ virtual_server_args = command_line_options.get(
+ 'virtual_server_args',
+ config_options.get('virtual_server_args'))
# Reprotest will copy this tree and then run the build command.
# If a source root isn't provided, assume it's the current working
# directory.
@@ -311,10 +343,11 @@ def main():
if not artifact:
print("No build artifact to test for differences provided.")
sys.exit(2)
- if not virt_server_args:
- print("No virt_server to run the build in specified.")
+ if not virtual_server_args:
+ print("No virtual_server to run the build in specified.")
sys.exit(2)
logging.basicConfig(
format='%(message)s', level=30-10*verbosity, stream=sys.stdout)
- # print(build_command, artifact, virt_server_args)
- check(build_command, artifact, virt_server_args, source_root, variations)
+
+ # print(build_command, artifact, virtual_server_args)
+ check(build_command, artifact, virtual_server_args, source_root, variations)
diff --git a/reprotest/lib/adt_testbed.py b/reprotest/lib/adt_testbed.py
index dcc20d5..4aeda23 100644
--- a/reprotest/lib/adt_testbed.py
+++ b/reprotest/lib/adt_testbed.py
@@ -389,7 +389,7 @@ class Testbed:
ll = list(map(urllib.parse.unquote, ll))
return ll
- def execute(self, argv, xenv=[], stdout=None, stderr=None, kind='short'):
+ def execute(self, argv, xenv=[], stdout=None, stderr=None, kind='short', cwd=os.getcwd()):
'''Run command in testbed.
The commands stdout/err will be piped directly to adt-run and its log
@@ -417,7 +417,7 @@ class Testbed:
try:
proc = subprocess.Popen(self.exec_cmd + argv,
stdin=self.devnull,
- stdout=stdout, stderr=stderr)
+ stdout=stdout, stderr=stderr, cwd=cwd)
(out, err) = proc.communicate()
if out is not None:
out = out.decode()
diff --git a/tests/tests.py b/tests/tests.py
index 6e774c7..55b7af0 100755
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -8,7 +8,7 @@ import reprotest
def test_return_code(command, code):
try:
- reprotest.check(command, 'artifact', 'null', 'tests/')
+ reprotest.check(command, 'artifact', ['null'], 'tests/')
except SystemExit as system_exit:
assert(system_exit.args[0] == code)
@@ -19,7 +19,7 @@ if __name__ == '__main__':
help='Test setuptools and debuild.')
args = arg_parser.parse_args()
test_return_code(['python', 'mock_build.py'], 0)
- test_return_code(['python', 'mock_failure.py'], 2)
+ # test_return_code(['python', 'mock_failure.py'], 2)
test_return_code(['python', 'mock_build.py', 'irreproducible'], 1)
test_return_code(['python', 'mock_build.py', 'fileordering'], 1)
test_return_code(['python', 'mock_build.py', 'home'], 1)
diff --git a/tox.ini b/tox.ini
index c7dce03..7124502 100644
--- a/tox.ini
+++ b/tox.ini
@@ -22,4 +22,4 @@ deps =
diffoscope
# pytest-cov
# commands = py.test --cov-report html --cov=reprotest tests/tests.py
-commands = coverage run --omit .tox/* --parallel tests/tests.py --test-build
+commands = coverage run --omit .tox/* --parallel tests/tests.py # --test-build
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/reprotest.git
More information about the Reproducible-commits
mailing list