Extend up -t to pass timeout to stop running containers

Signed-off-by: Travis Thieman <travis.thieman@gmail.com>
This commit is contained in:
Travis Thieman 2015-05-28 09:28:02 -04:00 committed by Daniel Nephin
parent 60351a8e07
commit c24d5380e6
5 changed files with 38 additions and 9 deletions

View File

@ -439,9 +439,9 @@ class TopLevelCommand(Command):
image needs to be updated. (EXPERIMENTAL) image needs to be updated. (EXPERIMENTAL)
--no-recreate If containers already exist, don't recreate them. --no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing --no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds -t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
for the shutdown. (default: 10) when attached or when containers are already
running. (default: 10)
""" """
insecure_registry = options['--allow-insecure-ssl'] insecure_registry = options['--allow-insecure-ssl']
detached = options['-d'] detached = options['-d']
@ -452,6 +452,7 @@ class TopLevelCommand(Command):
allow_recreate = not options['--no-recreate'] allow_recreate = not options['--no-recreate']
smart_recreate = options['--x-smart-recreate'] smart_recreate = options['--x-smart-recreate']
service_names = options['SERVICE'] service_names = options['SERVICE']
timeout = int(options['--timeout']) if options['--timeout'] is not None else None
project.up( project.up(
service_names=service_names, service_names=service_names,
@ -460,6 +461,7 @@ class TopLevelCommand(Command):
smart_recreate=smart_recreate, smart_recreate=smart_recreate,
insecure_registry=insecure_registry, insecure_registry=insecure_registry,
do_build=not options['--no-build'], do_build=not options['--no-build'],
timeout=timeout
) )
to_attach = [c for s in project.get_services(service_names) for c in s.containers()] to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
@ -477,8 +479,7 @@ class TopLevelCommand(Command):
signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGINT, handler)
print("Gracefully stopping... (press Ctrl+C again to force)") print("Gracefully stopping... (press Ctrl+C again to force)")
timeout = options.get('--timeout') params = {} if timeout is None else {'timeout': timeout}
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=service_names, **params) project.stop(service_names=service_names, **params)
def migrate_to_labels(self, project, _options): def migrate_to_labels(self, project, _options):

View File

@ -211,7 +211,8 @@ class Project(object):
allow_recreate=True, allow_recreate=True,
smart_recreate=False, smart_recreate=False,
insecure_registry=False, insecure_registry=False,
do_build=True): do_build=True,
timeout=None):
services = self.get_services(service_names, include_deps=start_deps) services = self.get_services(service_names, include_deps=start_deps)
@ -228,6 +229,7 @@ class Project(object):
plans[service.name], plans[service.name],
insecure_registry=insecure_registry, insecure_registry=insecure_registry,
do_build=do_build, do_build=do_build,
timeout=timeout
) )
] ]

View File

@ -311,7 +311,8 @@ class Service(object):
def execute_convergence_plan(self, def execute_convergence_plan(self,
plan, plan,
insecure_registry=False, insecure_registry=False,
do_build=True): do_build=True,
timeout=None):
(action, containers) = plan (action, containers) = plan
if action == 'create': if action == 'create':
@ -328,6 +329,7 @@ class Service(object):
self.recreate_container( self.recreate_container(
c, c,
insecure_registry=insecure_registry, insecure_registry=insecure_registry,
timeout=timeout
) )
for c in containers for c in containers
] ]
@ -349,7 +351,8 @@ class Service(object):
def recreate_container(self, def recreate_container(self,
container, container,
insecure_registry=False): insecure_registry=False,
timeout=None):
"""Recreate a container. """Recreate a container.
The original container is renamed to a temporary name so that data The original container is renamed to a temporary name so that data
@ -358,7 +361,8 @@ class Service(object):
""" """
log.info("Recreating %s..." % container.name) log.info("Recreating %s..." % container.name)
try: try:
container.stop() stop_params = {} if timeout is None else {'timeout': timeout}
container.stop(**stop_params)
except APIError as e: except APIError as e:
if (e.response.status_code == 500 if (e.response.status_code == 500
and e.explanation and e.explanation

View File

@ -162,6 +162,19 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(old_ids, new_ids) self.assertEqual(old_ids, new_ids)
def test_up_with_timeout(self):
self.command.dispatch(['up', '-d', '-t', '1'], None)
service = self.project.get_service('simple')
another = self.project.get_service('another')
self.assertEqual(len(service.containers()), 1)
self.assertEqual(len(another.containers()), 1)
# Ensure containers don't have stdin and stdout connected in -d mode
config = service.containers()[0].inspect()['Config']
self.assertFalse(config['AttachStderr'])
self.assertFalse(config['AttachStdout'])
self.assertFalse(config['AttachStdin'])
@patch('dockerpty.start') @patch('dockerpty.start')
def test_run_service_without_links(self, mock_stdout): def test_run_service_without_links(self, mock_stdout):
self.command.base_dir = 'tests/fixtures/links-composefile' self.command.base_dir = 'tests/fixtures/links-composefile'

View File

@ -254,6 +254,15 @@ class ServiceTest(unittest.TestCase):
new_container.start.assert_called_once_with() new_container.start.assert_called_once_with()
mock_container.remove.assert_called_once_with() mock_container.remove.assert_called_once_with()
@mock.patch('compose.service.Container', autospec=True)
def test_recreate_container_with_timeout(self, _):
mock_container = mock.create_autospec(Container)
self.mock_client.inspect_image.return_value = {'Id': 'abc123'}
service = Service('foo', client=self.mock_client, image='someimage')
service.recreate_container(mock_container, timeout=1)
mock_container.stop.assert_called_once_with(timeout=1)
def test_parse_repository_tag(self): def test_parse_repository_tag(self):
self.assertEqual(parse_repository_tag("root"), ("root", "")) self.assertEqual(parse_repository_tag("root"), ("root", ""))
self.assertEqual(parse_repository_tag("root:tag"), ("root", "tag")) self.assertEqual(parse_repository_tag("root:tag"), ("root", "tag"))