Add support for multiple images

This patch adds support for running tests on multiple images. Instead of
parsing just 1 image, it attempts to always parse multiple images from
config files and/or CLI.

To run dox on multiple images from the CLI, you'll have to:

    $ dox -i infra/trusty,infra/f20

The tox.ini `docker:images` option is now a comma-separated list of
images, whereas the dox.yml one is an array of images.

Change-Id: I124c299b208be5c31c63187af754705c48de5693
This commit is contained in:
Flavio Percoco 2014-09-08 09:16:35 +02:00 committed by Flavio Percoco
parent 2ea441b29d
commit de82bcdf77
7 changed files with 49 additions and 41 deletions

View File

@ -1,4 +1,5 @@
image: infra/trusty images:
- infra/trusty
add: add:
- requirements.txt - requirements.txt
- test-requirements.txt - test-requirements.txt

View File

@ -14,6 +14,7 @@
# limitations under the License. # limitations under the License.
import argparse import argparse
import functools
import logging import logging
import dox.commands import dox.commands
@ -46,8 +47,8 @@ def parse_args():
parser.add_argument(dest='extra_args', nargs='*', parser.add_argument(dest='extra_args', nargs='*',
help='args to append to command, or command to run' help='args to append to command, or command to run'
' if -c is given') ' if -c is given')
parser.add_argument('-i', '--image', dest='image', parser.add_argument('-i', '--images', dest='images',
help='Base image to use') help='Base images to use')
parser.add_argument('-c', '--command', dest='command', default=False, parser.add_argument('-c', '--command', dest='command', default=False,
action='store_true', action='store_true',
help='Treat arguments as the entire command to run') help='Treat arguments as the entire command to run')
@ -77,9 +78,10 @@ def main():
def run_dox(args): def run_dox(args):
# Get Image # Get Image
image = args.image if args.images is None:
if args.image is None: images = dox.images.get_images()
image = dox.images.get_image() else:
images = args.images.split(',')
# Get Command # Get Command
if args.command: if args.command:
@ -89,7 +91,9 @@ def run_dox(args):
# Run # Run
try: try:
return dox.runner.Runner(args).run(image, command) run = functools.partial(dox.runner.Runner(args).run,
command=command)
map(run, images)
except Exception: except Exception:
logger.error( logger.error(
"Operation failed, aborting dox.", exc_info=args.debug) "Operation failed, aborting dox.", exc_info=args.debug)

View File

@ -43,8 +43,8 @@ class DoxYaml(object):
def exists(self): def exists(self):
return os.path.exists('dox.yml') return os.path.exists('dox.yml')
def get_image(self, image): def get_images(self):
return self._open_dox_yaml().get('image', image) return self._open_dox_yaml().get('images', [])
def get_commands(self, extra_args): def get_commands(self, extra_args):
return " ".join([self._open_dox_yaml().get('commands')] + extra_args) return " ".join([self._open_dox_yaml().get('commands')] + extra_args)

View File

@ -43,11 +43,10 @@ class ToxIni(object):
def exists(self): def exists(self):
return os.path.exists('tox.ini') return os.path.exists('tox.ini')
def get_image(self, image): def get_images(self):
ini = self._open_tox_ini() ini = self._open_tox_ini()
if ini.has_option('docker', 'image'): if ini.has_option('docker', 'images'):
image = ini.get('docker', 'image') return ini.get('docker', 'images', '').split(',')
return image
def get_commands(self, extra_args): def get_commands(self, extra_args):
ini = self._open_tox_ini() ini = self._open_tox_ini()

View File

@ -14,7 +14,7 @@
# limitations under the License. # limitations under the License.
__all__ = [ __all__ = [
'get_image', 'get_images',
] ]
import dox.config.dockerfile import dox.config.dockerfile
@ -22,21 +22,24 @@ import dox.config.dox_yaml
import dox.config.tox_ini import dox.config.tox_ini
def get_image(): def get_images():
'''Examine the local environment and figure out where we should run.''' '''Examine the local environment and figure out where we should run.'''
dockerfile = dox.config.dockerfile.get_dockerfile() dockerfile = dox.config.dockerfile.get_dockerfile()
dox_yaml = dox.config.dox_yaml.get_dox_yaml() dox_yaml = dox.config.dox_yaml.get_dox_yaml()
tox_ini = dox.config.tox_ini.get_tox_ini() tox_ini = dox.config.tox_ini.get_tox_ini()
# Set default image value
if dockerfile.exists(): if dockerfile.exists():
image = None default_images = []
else: else:
image = 'ubuntu' # NOTE(flaper87): We should probably raise
# `RuntimeError` if no image was specified
default_images = ['ubuntu']
images = []
if dox_yaml.exists(): if dox_yaml.exists():
image = dox_yaml.get_image(image) images = dox_yaml.get_images()
elif tox_ini.exists(): elif tox_ini.exists():
image = tox_ini.get_image(image) images = tox_ini.get_images()
return image
return images or default_images

View File

@ -29,12 +29,12 @@ from dox.tests import base
def get_fake_image(value): def get_fake_image(value):
if value: if value is not None:
def fake_value(self, image): def fake_value(self):
return value return value
else: else:
def fake_value(self, image): def fake_value(self):
return image return ['ubuntu']
return fake_value return fake_value
@ -43,37 +43,38 @@ class TestImages(base.TestCase):
scenarios = [ scenarios = [
('have_dockerfile', dict( ('have_dockerfile', dict(
dockerfile=True, tox_ini=False, dox_yaml=False, dockerfile=True, tox_ini=False, dox_yaml=False,
tox_value=None, dox_value=None, image=None)), tox_value=[], dox_value=[], images=[])),
('no_dockerfile', dict( ('no_dockerfile', dict(
dockerfile=False, tox_ini=False, dox_yaml=False, dockerfile=False, tox_ini=False, dox_yaml=False,
tox_value=None, dox_value=None, image='ubuntu')), tox_value=[], dox_value=[], images=['ubuntu'])),
('tox_no_docker', dict( ('tox_no_docker', dict(
dockerfile=False, tox_ini=True, dox_yaml=False, dockerfile=False, tox_ini=True, dox_yaml=False,
tox_value=None, dox_value=None, image='ubuntu')), tox_value=[], dox_value=[], images=['ubuntu'])),
('tox_docker', dict( ('tox_docker', dict(
dockerfile=False, tox_ini=True, dox_yaml=False, dockerfile=False, tox_ini=True, dox_yaml=False,
tox_value='tox_docker', dox_value=None, image='tox_docker')), tox_value=['tox_docker'], dox_value=[], images=['tox_docker'])),
('dox_image', dict( ('dox_image', dict(
dockerfile=False, tox_ini=False, dox_yaml=True, dockerfile=False, tox_ini=False, dox_yaml=True,
tox_value=None, dox_value=None, image='ubuntu')), tox_value=[], dox_value=[], images=['ubuntu'])),
('dox_no_image', dict( ('dox_no_image', dict(
dockerfile=False, tox_ini=False, dox_yaml=True, dockerfile=False, tox_ini=False, dox_yaml=True,
tox_value=None, dox_value='dox_value', image='dox_value')), tox_value=[], dox_value=['dox_value'], images=['dox_value'])),
('both_dox_wins', dict( ('both_dox_wins', dict(
dockerfile=False, tox_ini=True, dox_yaml=True, dockerfile=False, tox_ini=True, dox_yaml=True,
tox_value='tox_wins', dox_value='dox_wins', image='dox_wins')), tox_value=['tox_wins'], dox_value=['dox_wins'],
images=['dox_wins'])),
('both_no_dox', dict( ('both_no_dox', dict(
dockerfile=False, tox_ini=True, dox_yaml=True, dockerfile=False, tox_ini=True, dox_yaml=True,
tox_value='tox_wins', dox_value=None, image='ubuntu')), tox_value=['tox_wins'], dox_value=[], images=['ubuntu'])),
('both_dockerfile_passthru', dict( ('both_dockerfile_passthru', dict(
dockerfile=True, tox_ini=True, dox_yaml=True, dockerfile=True, tox_ini=True, dox_yaml=True,
tox_value=None, dox_value=None, image=None)), tox_value=[], dox_value=[], images=[])),
('all_dockerfile_dox_override', dict( ('all_dockerfile_dox_override', dict(
dockerfile=True, tox_ini=True, dox_yaml=True, dockerfile=True, tox_ini=True, dox_yaml=True,
tox_value=None, dox_value='dox_wins', image='dox_wins')), tox_value=[], dox_value=['dox_wins'], images=['dox_wins'])),
('all_dockerfile_tox_loses', dict( ('all_dockerfile_tox_loses', dict(
dockerfile=True, tox_ini=True, dox_yaml=True, dockerfile=True, tox_ini=True, dox_yaml=True,
tox_value='tox_wins', dox_value=None, image=None)), tox_value=['tox_wins'], dox_value=[], images=[])),
] ]
def setUp(self): def setUp(self):
@ -88,15 +89,15 @@ class TestImages(base.TestCase):
'dox.config.tox_ini.ToxIni.exists', 'dox.config.tox_ini.ToxIni.exists',
base.bool_to_fake(self.tox_ini))) base.bool_to_fake(self.tox_ini)))
self.useFixture(fixtures.MonkeyPatch( self.useFixture(fixtures.MonkeyPatch(
'dox.config.dox_yaml.DoxYaml.get_image', 'dox.config.dox_yaml.DoxYaml.get_images',
get_fake_image(self.dox_value))) get_fake_image(self.dox_value)))
self.useFixture(fixtures.MonkeyPatch( self.useFixture(fixtures.MonkeyPatch(
'dox.config.tox_ini.ToxIni.get_image', 'dox.config.tox_ini.ToxIni.get_images',
get_fake_image(self.tox_value))) get_fake_image(self.tox_value)))
def test_images(self): def test_images(self):
image = images.get_image() image = images.get_images()
self.assertEqual(image, self.image) self.assertEqual(image, self.images)
def load_tests(loader, in_tests, pattern): def load_tests(loader, in_tests, pattern):

View File

@ -30,4 +30,4 @@ builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
[docker] [docker]
image = infra/trusty images = infra/trusty