Fix signal handlers by moving shutdown logic out of handler.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2015-12-28 16:57:55 -05:00
parent ea8cc1c3dc
commit 778c213dfc
4 changed files with 40 additions and 28 deletions

View File

@ -3,7 +3,6 @@ from __future__ import unicode_literals
import logging import logging
import re import re
import signal
import sys import sys
from inspect import getdoc from inspect import getdoc
from operator import attrgetter from operator import attrgetter
@ -12,6 +11,7 @@ import yaml
from docker.errors import APIError from docker.errors import APIError
from requests.exceptions import ReadTimeout from requests.exceptions import ReadTimeout
from . import signals
from .. import __version__ from .. import __version__
from ..config import config from ..config import config
from ..config import ConfigurationError from ..config import ConfigurationError
@ -655,20 +655,19 @@ def run_one_off_container(container_options, project, service, options):
if options['--rm']: if options['--rm']:
project.client.remove_container(container.id, force=True) project.client.remove_container(container.id, force=True)
def force_shutdown(signal, frame): signals.set_signal_handler_to_shutdown()
try:
try:
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
except signals.ShutdownException:
project.client.stop(container.id)
exit_code = 1
except signals.ShutdownException:
project.client.kill(container.id) project.client.kill(container.id)
remove_container(force=True) remove_container(force=True)
sys.exit(2) sys.exit(2)
def shutdown(signal, frame):
set_signal_handler(force_shutdown)
project.client.stop(container.id)
remove_container()
sys.exit(1)
set_signal_handler(shutdown)
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
remove_container() remove_container()
sys.exit(exit_code) sys.exit(exit_code)
@ -683,25 +682,19 @@ def build_log_printer(containers, service_names, monochrome):
def attach_to_logs(project, log_printer, service_names, timeout): def attach_to_logs(project, log_printer, service_names, timeout):
print("Attaching to", list_containers(log_printer.containers))
signals.set_signal_handler_to_shutdown()
def force_shutdown(signal, frame): try:
try:
log_printer.run()
except signals.ShutdownException:
print("Gracefully stopping... (press Ctrl+C again to force)")
project.stop(service_names=service_names, timeout=timeout)
except signals.ShutdownException:
project.kill(service_names=service_names) project.kill(service_names=service_names)
sys.exit(2) sys.exit(2)
def shutdown(signal, frame):
set_signal_handler(force_shutdown)
print("Gracefully stopping... (press Ctrl+C again to force)")
project.stop(service_names=service_names, timeout=timeout)
print("Attaching to", list_containers(log_printer.containers))
set_signal_handler(shutdown)
log_printer.run()
def set_signal_handler(handler):
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
def list_containers(containers): def list_containers(containers):
return ", ".join(c.name for c in containers) return ", ".join(c.name for c in containers)

18
compose/cli/signals.py Normal file
View File

@ -0,0 +1,18 @@
import signal
class ShutdownException(Exception):
pass
def shutdown(signal, frame):
raise ShutdownException()
def set_signal_handler(handler):
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
def set_signal_handler_to_shutdown():
set_signal_handler(shutdown)

View File

@ -86,7 +86,8 @@ class ContainerStateCondition(object):
return False return False
def __str__(self): def __str__(self):
return "waiting for container to have state %s" % self.expected state = 'running' if self.running else 'stopped'
return "waiting for container to be %s" % state
class CLITestCase(DockerClientTestCase): class CLITestCase(DockerClientTestCase):

View File

@ -54,7 +54,7 @@ class CLIMainTestCase(unittest.TestCase):
service_names = ['web', 'db'] service_names = ['web', 'db']
timeout = 12 timeout = 12
with mock.patch('compose.cli.main.signal', autospec=True) as mock_signal: with mock.patch('compose.cli.main.signals.signal', autospec=True) as mock_signal:
attach_to_logs(project, log_printer, service_names, timeout) attach_to_logs(project, log_printer, service_names, timeout)
assert mock_signal.signal.mock_calls == [ assert mock_signal.signal.mock_calls == [