|
@@ -858,8 +858,11 @@ class TopLevelCommand(object):
|
|
|
running. (default: 10)
|
|
|
--remove-orphans Remove containers for services not
|
|
|
defined in the Compose file
|
|
|
+ --exit-code-from SERVICE Return the exit code of the selected service container.
|
|
|
+ Requires --abort-on-container-exit.
|
|
|
"""
|
|
|
start_deps = not options['--no-deps']
|
|
|
+ exit_value_from = exitval_from_opts(options, self.project)
|
|
|
cascade_stop = options['--abort-on-container-exit']
|
|
|
service_names = options['SERVICE']
|
|
|
timeout = timeout_from_opts(options)
|
|
@@ -882,9 +885,11 @@ class TopLevelCommand(object):
|
|
|
if detached:
|
|
|
return
|
|
|
|
|
|
+ attached_containers = filter_containers_to_service_names(to_attach, service_names)
|
|
|
+
|
|
|
log_printer = log_printer_from_project(
|
|
|
self.project,
|
|
|
- filter_containers_to_service_names(to_attach, service_names),
|
|
|
+ attached_containers,
|
|
|
options['--no-color'],
|
|
|
{'follow': True},
|
|
|
cascade_stop,
|
|
@@ -894,12 +899,34 @@ class TopLevelCommand(object):
|
|
|
|
|
|
if cascade_stop:
|
|
|
print("Aborting on container exit...")
|
|
|
+
|
|
|
exit_code = 0
|
|
|
- for e in self.project.containers(service_names=options['SERVICE'], stopped=True):
|
|
|
- if (not e.is_running and cascade_starter == e.name):
|
|
|
- if not e.exit_code == 0:
|
|
|
- exit_code = e.exit_code
|
|
|
- break
|
|
|
+ if exit_value_from:
|
|
|
+ candidates = filter(
|
|
|
+ lambda c: c.service == exit_value_from,
|
|
|
+ attached_containers)
|
|
|
+ if not candidates:
|
|
|
+ log.error(
|
|
|
+ 'No containers matching the spec "{0}" '
|
|
|
+ 'were run.'.format(exit_value_from)
|
|
|
+ )
|
|
|
+ exit_code = 2
|
|
|
+ elif len(candidates) > 1:
|
|
|
+ exit_values = filter(
|
|
|
+ lambda e: e != 0,
|
|
|
+ [c.inspect()['State']['ExitCode'] for c in candidates]
|
|
|
+ )
|
|
|
+
|
|
|
+ exit_code = exit_values[0]
|
|
|
+ else:
|
|
|
+ exit_code = candidates[0].inspect()['State']['ExitCode']
|
|
|
+ else:
|
|
|
+ for e in self.project.containers(service_names=options['SERVICE'], stopped=True):
|
|
|
+ if (not e.is_running and cascade_starter == e.name):
|
|
|
+ if not e.exit_code == 0:
|
|
|
+ exit_code = e.exit_code
|
|
|
+ break
|
|
|
+
|
|
|
self.project.stop(service_names=service_names, timeout=timeout)
|
|
|
sys.exit(exit_code)
|
|
|
|
|
@@ -939,6 +966,19 @@ def timeout_from_opts(options):
|
|
|
return None if timeout is None else int(timeout)
|
|
|
|
|
|
|
|
|
+def exitval_from_opts(options, project):
|
|
|
+ exit_value_from = options.get('--exit-code-from')
|
|
|
+ if exit_value_from:
|
|
|
+ if not options.get('--abort-on-container-exit'):
|
|
|
+ log.warn('using --exit-code-from implies --abort-on-container-exit')
|
|
|
+ options['--abort-on-container-exit'] = True
|
|
|
+ if exit_value_from not in [s.name for s in project.get_services()]:
|
|
|
+ log.error('No service named "%s" was found in your compose file.',
|
|
|
+ exit_value_from)
|
|
|
+ sys.exit(2)
|
|
|
+ return exit_value_from
|
|
|
+
|
|
|
+
|
|
|
def image_type_from_opt(flag, value):
|
|
|
if not value:
|
|
|
return ImageType.none
|