Handle both SIGINT and SIGTERM for docker-compose run.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2015-11-10 19:43:05 -05:00
parent ea4230e7a2
commit 6236bb0019
2 changed files with 102 additions and 44 deletions

View File

@ -368,7 +368,6 @@ class TopLevelCommand(DocoptCommand):
allocates a TTY.
"""
service = project.get_service(options['SERVICE'])
detach = options['-d']
if IS_WINDOWS_PLATFORM and not detach:
@ -380,22 +379,6 @@ class TopLevelCommand(DocoptCommand):
if options['--allow-insecure-ssl']:
log.warn(INSECURE_SSL_WARNING)
if not options['--no-deps']:
deps = service.get_linked_service_names()
if len(deps) > 0:
project.up(
service_names=deps,
start_deps=True,
strategy=ConvergenceStrategy.never,
)
elif project.use_networking:
project.ensure_network_exists()
tty = True
if detach or options['-T'] or not sys.stdin.isatty():
tty = False
if options['COMMAND']:
command = [options['COMMAND']] + options['ARGS']
else:
@ -403,7 +386,7 @@ class TopLevelCommand(DocoptCommand):
container_options = {
'command': command,
'tty': tty,
'tty': not (detach or options['-T'] or not sys.stdin.isatty()),
'stdin_open': not detach,
'detach': detach,
}
@ -435,31 +418,7 @@ class TopLevelCommand(DocoptCommand):
if options['--name']:
container_options['name'] = options['--name']
try:
container = service.create_container(
quiet=True,
one_off=True,
**container_options
)
except APIError as e:
legacy.check_for_legacy_containers(
project.client,
project.name,
[service.name],
allow_one_off=False,
)
raise e
if detach:
container.start()
print(container.name)
else:
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
if options['--rm']:
project.client.remove_container(container.id)
sys.exit(exit_code)
run_one_off_container(container_options, project, service, options)
def scale(self, project, options):
"""
@ -647,6 +606,58 @@ def convergence_strategy_from_opts(options):
return ConvergenceStrategy.changed
def run_one_off_container(container_options, project, service, options):
if not options['--no-deps']:
deps = service.get_linked_service_names()
if deps:
project.up(
service_names=deps,
start_deps=True,
strategy=ConvergenceStrategy.never)
if project.use_networking:
project.ensure_network_exists()
try:
container = service.create_container(
quiet=True,
one_off=True,
**container_options)
except APIError:
legacy.check_for_legacy_containers(
project.client,
project.name,
[service.name],
allow_one_off=False)
raise
if options['-d']:
container.start()
print(container.name)
return
def remove_container(force=False):
if options['--rm']:
project.client.remove_container(container.id, force=True)
def force_shutdown(signal, frame):
project.client.kill(container.id)
remove_container(force=True)
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()
sys.exit(exit_code)
def build_log_printer(containers, service_names, monochrome):
if service_names:
containers = [
@ -657,7 +668,6 @@ def build_log_printer(containers, service_names, monochrome):
def attach_to_logs(project, log_printer, service_names, timeout):
print("Attaching to", list_containers(log_printer.containers))
def force_shutdown(signal, frame):
project.kill(service_names=service_names)
@ -668,6 +678,7 @@ def attach_to_logs(project, log_printer, service_names, timeout):
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()

View File

@ -8,6 +8,8 @@ import time
from collections import namedtuple
from operator import attrgetter
from docker import errors
from .. import mock
from compose.cli.command import get_project
from compose.cli.docker_client import docker_client
@ -61,6 +63,25 @@ class ContainerCountCondition(object):
return "waiting for counter count == %s" % self.expected
class ContainerStateCondition(object):
def __init__(self, client, name, running):
self.client = client
self.name = name
self.running = running
# State.Running == true
def __call__(self):
try:
container = self.client.inspect_container(self.name)
return container['State']['Running'] == self.running
except errors.APIError:
return False
def __str__(self):
return "waiting for container to have state %s" % self.expected
class CLITestCase(DockerClientTestCase):
def setUp(self):
@ -554,6 +575,32 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(networks), 1)
self.assertEqual(container.human_readable_command, u'true')
def test_run_handles_sigint(self):
proc = start_process(self.base_dir, ['run', '-T', 'simple', 'top'])
wait_on_condition(ContainerStateCondition(
self.project.client,
'simplecomposefile_simple_run_1',
running=True))
os.kill(proc.pid, signal.SIGINT)
wait_on_condition(ContainerStateCondition(
self.project.client,
'simplecomposefile_simple_run_1',
running=False))
def test_run_handles_sigterm(self):
proc = start_process(self.base_dir, ['run', '-T', 'simple', 'top'])
wait_on_condition(ContainerStateCondition(
self.project.client,
'simplecomposefile_simple_run_1',
running=True))
os.kill(proc.pid, signal.SIGTERM)
wait_on_condition(ContainerStateCondition(
self.project.client,
'simplecomposefile_simple_run_1',
running=False))
def test_rm(self):
service = self.project.get_service('simple')
service.create_container()