Merge pull request #2130 from scipetr/master

Add flag for stops all containers if any container was stopped.
This commit is contained in:
Daniel Nephin 2016-01-13 15:55:19 -05:00
commit d56f64c30a
6 changed files with 68 additions and 33 deletions

View File

@ -13,10 +13,11 @@ from compose.utils import split_buffer
class LogPrinter(object): class LogPrinter(object):
"""Print logs from many containers to a single output stream.""" """Print logs from many containers to a single output stream."""
def __init__(self, containers, output=sys.stdout, monochrome=False): def __init__(self, containers, output=sys.stdout, monochrome=False, cascade_stop=False):
self.containers = containers self.containers = containers
self.output = utils.get_output_stream(output) self.output = utils.get_output_stream(output)
self.monochrome = monochrome self.monochrome = monochrome
self.cascade_stop = cascade_stop
def run(self): def run(self):
if not self.containers: if not self.containers:
@ -24,7 +25,7 @@ class LogPrinter(object):
prefix_width = max_name_width(self.containers) prefix_width = max_name_width(self.containers)
generators = list(self._make_log_generators(self.monochrome, prefix_width)) generators = list(self._make_log_generators(self.monochrome, prefix_width))
for line in Multiplexer(generators).loop(): for line in Multiplexer(generators, cascade_stop=self.cascade_stop).loop():
self.output.write(line) self.output.write(line)
self.output.flush() self.output.flush()

View File

@ -590,25 +590,33 @@ class TopLevelCommand(DocoptCommand):
Usage: up [options] [SERVICE...] Usage: up [options] [SERVICE...]
Options: Options:
-d Detached mode: Run containers in the background, -d Detached mode: Run containers in the background,
print new container names. print new container names.
--no-color Produce monochrome output. Incompatible with --abort-on-container-exit.
--no-deps Don't start linked services. --no-color Produce monochrome output.
--force-recreate Recreate containers even if their configuration and --no-deps Don't start linked services.
image haven't changed. Incompatible with --no-recreate. --force-recreate Recreate containers even if their configuration
--no-recreate If containers already exist, don't recreate them. and image haven't changed.
Incompatible with --force-recreate. Incompatible with --no-recreate.
--no-build Don't build an image, even if it's missing --no-recreate If containers already exist, don't recreate them.
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown Incompatible with --force-recreate.
when attached or when containers are already --no-build Don't build an image, even if it's missing
running. (default: 10) --abort-on-container-exit Stops all containers if any container was stopped.
Incompatible with -d.
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
when attached or when containers are already
running. (default: 10)
""" """
monochrome = options['--no-color'] monochrome = options['--no-color']
start_deps = not options['--no-deps'] start_deps = not options['--no-deps']
cascade_stop = options['--abort-on-container-exit']
service_names = options['SERVICE'] service_names = options['SERVICE']
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT) timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
detached = options.get('-d') detached = options.get('-d')
if detached and cascade_stop:
raise UserError("--abort-on-container-exit and -d cannot be combined.")
to_attach = project.up( to_attach = project.up(
service_names=service_names, service_names=service_names,
start_deps=start_deps, start_deps=start_deps,
@ -619,7 +627,7 @@ class TopLevelCommand(DocoptCommand):
) )
if not detached: if not detached:
log_printer = build_log_printer(to_attach, service_names, monochrome) log_printer = build_log_printer(to_attach, service_names, monochrome, cascade_stop)
attach_to_logs(project, log_printer, service_names, timeout) attach_to_logs(project, log_printer, service_names, timeout)
def version(self, project, options): def version(self, project, options):
@ -695,13 +703,13 @@ def run_one_off_container(container_options, project, service, options):
sys.exit(exit_code) sys.exit(exit_code)
def build_log_printer(containers, service_names, monochrome): def build_log_printer(containers, service_names, monochrome, cascade_stop):
if service_names: if service_names:
containers = [ containers = [
container container
for container in containers if container.service in service_names for container in containers if container.service in service_names
] ]
return LogPrinter(containers, monochrome=monochrome) return LogPrinter(containers, monochrome=monochrome, cascade_stop=cascade_stop)
def attach_to_logs(project, log_printer, service_names, timeout): def attach_to_logs(project, log_printer, service_names, timeout):

View File

@ -20,8 +20,9 @@ class Multiplexer(object):
parallel and yielding results as they come in. parallel and yielding results as they come in.
""" """
def __init__(self, iterators): def __init__(self, iterators, cascade_stop=False):
self.iterators = iterators self.iterators = iterators
self.cascade_stop = cascade_stop
self._num_running = len(iterators) self._num_running = len(iterators)
self.queue = Queue() self.queue = Queue()
@ -36,7 +37,10 @@ class Multiplexer(object):
raise exception raise exception
if item is STOP: if item is STOP:
self._num_running -= 1 if self.cascade_stop is True:
break
else:
self._num_running -= 1
else: else:
yield item yield item
except Empty: except Empty:

View File

@ -15,18 +15,22 @@ parent = "smn_compose_cli"
Usage: up [options] [SERVICE...] Usage: up [options] [SERVICE...]
Options: Options:
-d Detached mode: Run containers in the background, -d Detached mode: Run containers in the background,
print new container names. print new container names.
--no-color Produce monochrome output. Incompatible with --abort-on-container-exit.
--no-deps Don't start linked services. --no-color Produce monochrome output.
--force-recreate Recreate containers even if their configuration and --no-deps Don't start linked services.
image haven't changed. Incompatible with --no-recreate. --force-recreate Recreate containers even if their configuration
--no-recreate If containers already exist, don't recreate them. and image haven't changed.
Incompatible with --force-recreate. Incompatible with --no-recreate.
--no-build Don't build an image, even if it's missing --no-recreate If containers already exist, don't recreate them.
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown Incompatible with --force-recreate.
when attached or when containers are already --no-build Don't build an image, even if it's missing
running. (default: 10) --abort-on-container-exit Stops all containers if any container was stopped.
Incompatible with -d.
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
when attached or when containers are already
running. (default: 10)
``` ```
Builds, (re)creates, starts, and attaches to containers for a service. Builds, (re)creates, starts, and attaches to containers for a service.

View File

@ -36,7 +36,7 @@ class CLIMainTestCase(unittest.TestCase):
mock_container('another', 1), mock_container('another', 1),
] ]
service_names = ['web', 'db'] service_names = ['web', 'db']
log_printer = build_log_printer(containers, service_names, True) log_printer = build_log_printer(containers, service_names, True, False)
self.assertEqual(log_printer.containers, containers[:3]) self.assertEqual(log_printer.containers, containers[:3])
def test_build_log_printer_all_services(self): def test_build_log_printer_all_services(self):
@ -46,7 +46,7 @@ class CLIMainTestCase(unittest.TestCase):
mock_container('other', 1), mock_container('other', 1),
] ]
service_names = [] service_names = []
log_printer = build_log_printer(containers, service_names, True) log_printer = build_log_printer(containers, service_names, True, False)
self.assertEqual(log_printer.containers, containers) self.assertEqual(log_printer.containers, containers)
def test_attach_to_logs(self): def test_attach_to_logs(self):

View File

@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals from __future__ import unicode_literals
import unittest import unittest
from time import sleep
from compose.cli.multiplexer import Multiplexer from compose.cli.multiplexer import Multiplexer
@ -46,3 +47,20 @@ class MultiplexerTest(unittest.TestCase):
with self.assertRaises(Problem): with self.assertRaises(Problem):
list(mux.loop()) list(mux.loop())
def test_cascade_stop(self):
mux = Multiplexer([
((lambda x: sleep(0.01) or x)(x) for x in ['after 0.01 sec T1',
'after 0.02 sec T1',
'after 0.03 sec T1']),
((lambda x: sleep(0.02) or x)(x) for x in ['after 0.02 sec T2',
'after 0.04 sec T2',
'after 0.06 sec T2']),
], cascade_stop=True)
self.assertEqual(
['after 0.01 sec T1',
'after 0.02 sec T1',
'after 0.02 sec T2',
'after 0.03 sec T1'],
sorted(list(mux.loop())))