From bab8b3985e02a71373f5d6f753485ac7a08c3092 Mon Sep 17 00:00:00 2001 From: Collins Abitekaniza Date: Thu, 10 Jan 2019 13:48:42 +0300 Subject: [PATCH 1/3] check for started containers only on service_start Signed-off-by: Collins Abitekaniza --- compose/parallel.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compose/parallel.py b/compose/parallel.py index 32ee602f4..dbf4845b8 100644 --- a/compose/parallel.py +++ b/compose/parallel.py @@ -43,14 +43,15 @@ class GlobalLimit(object): cls.global_limiter = Semaphore(value) -def parallel_execute_watch(events, writer, errors, results, msg, get_name): +def parallel_execute_watch(events, writer, errors, results, msg, get_name, func_name): """ Watch events from a parallel execution, update status and fill errors and results. Returns exception to re-raise. """ error_to_reraise = None for obj, result, exception in events: if exception is None: - if callable(getattr(obj, 'containers', None)) and not obj.containers(): + if func_name == 'start_service' and ( + callable(getattr(obj, 'containers', None)) and not obj.containers()): # If service has no containers started writer.write(msg, get_name(obj), 'failed', red) else: @@ -100,7 +101,8 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None): errors = {} results = [] - error_to_reraise = parallel_execute_watch(events, writer, errors, results, msg, get_name) + error_to_reraise = parallel_execute_watch( + events, writer, errors, results, msg, get_name, func.__name__) for obj_name, error in errors.items(): stream.write("\nERROR: for {} {}\n".format(obj_name, error)) From 325637d9d5939a82418a25360934b525dd601c20 Mon Sep 17 00:00:00 2001 From: Collins Abitekaniza Date: Thu, 10 Jan 2019 14:49:30 +0300 Subject: [PATCH 2/3] test image pull done Signed-off-by: Collins Abitekaniza --- compose/parallel.py | 2 +- tests/acceptance/cli_test.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compose/parallel.py b/compose/parallel.py index dbf4845b8..d6af51694 100644 --- a/compose/parallel.py +++ b/compose/parallel.py @@ -102,7 +102,7 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None): errors = {} results = [] error_to_reraise = parallel_execute_watch( - events, writer, errors, results, msg, get_name, func.__name__) + events, writer, errors, results, msg, get_name, getattr(func, '__name__', None)) for obj_name, error in errors.items(): stream.write("\nERROR: for {} {}\n".format(obj_name, error)) diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 1dc9616a5..9334a29fb 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -611,6 +611,11 @@ class CLITestCase(DockerClientTestCase): assert 'Pulling simple' in result.stderr assert 'Pulling another' in result.stderr + def test_pull_done(self): + result = self.dispatch(['pull']) + assert 'Pulling simple' in result.stderr + assert 'done' in result.stderr + def test_pull_with_digest(self): result = self.dispatch(['-f', 'digest.yml', 'pull', '--no-parallel']) From 2ed171cae94ef5ac9d2eeeb683b217e380e93b81 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 10 Jan 2019 15:47:44 -0800 Subject: [PATCH 3/3] Bring zero container check up in the call stack Signed-off-by: Joffrey F --- compose/parallel.py | 13 +++++++------ compose/project.py | 1 + tests/acceptance/cli_test.py | 5 +---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compose/parallel.py b/compose/parallel.py index d6af51694..e242a318a 100644 --- a/compose/parallel.py +++ b/compose/parallel.py @@ -43,16 +43,14 @@ class GlobalLimit(object): cls.global_limiter = Semaphore(value) -def parallel_execute_watch(events, writer, errors, results, msg, get_name, func_name): +def parallel_execute_watch(events, writer, errors, results, msg, get_name, fail_check): """ Watch events from a parallel execution, update status and fill errors and results. Returns exception to re-raise. """ error_to_reraise = None for obj, result, exception in events: if exception is None: - if func_name == 'start_service' and ( - callable(getattr(obj, 'containers', None)) and not obj.containers()): - # If service has no containers started + if fail_check is not None and fail_check(obj): writer.write(msg, get_name(obj), 'failed', red) else: writer.write(msg, get_name(obj), 'done', green) @@ -77,12 +75,14 @@ def parallel_execute_watch(events, writer, errors, results, msg, get_name, func_ return error_to_reraise -def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None): +def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None, fail_check=None): """Runs func on objects in parallel while ensuring that func is ran on object only after it is ran on all its dependencies. get_deps called on object must return a collection with its dependencies. get_name called on object must return its name. + fail_check is an additional failure check for cases that should display as a failure + in the CLI logs, but don't raise an exception (such as attempting to start 0 containers) """ objects = list(objects) stream = get_output_stream(sys.stderr) @@ -102,7 +102,8 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None): errors = {} results = [] error_to_reraise = parallel_execute_watch( - events, writer, errors, results, msg, get_name, getattr(func, '__name__', None)) + events, writer, errors, results, msg, get_name, fail_check + ) for obj_name, error in errors.items(): stream.write("\nERROR: for {} {}\n".format(obj_name, error)) diff --git a/compose/project.py b/compose/project.py index 5c4ce6e17..a7f2aa057 100644 --- a/compose/project.py +++ b/compose/project.py @@ -280,6 +280,7 @@ class Project(object): operator.attrgetter('name'), 'Starting', get_deps, + fail_check=lambda obj: not obj.containers(), ) return containers diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 9334a29fb..5142f96eb 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -610,11 +610,8 @@ class CLITestCase(DockerClientTestCase): result = self.dispatch(['pull']) assert 'Pulling simple' in result.stderr assert 'Pulling another' in result.stderr - - def test_pull_done(self): - result = self.dispatch(['pull']) - assert 'Pulling simple' in result.stderr assert 'done' in result.stderr + assert 'failed' not in result.stderr def test_pull_with_digest(self): result = self.dispatch(['-f', 'digest.yml', 'pull', '--no-parallel'])