mirror of
				https://github.com/docker/compose.git
				synced 2025-11-04 05:34:09 +01:00 
			
		
		
		
	Fix race condition with up and setting signal handlers.
Also print stdout on wait_for_container(). Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
		
							parent
							
								
									e925b8272b
								
							
						
					
					
						commit
						bbaae11a0f
					
				@ -2,6 +2,7 @@ from __future__ import absolute_import
 | 
				
			|||||||
from __future__ import print_function
 | 
					from __future__ import print_function
 | 
				
			||||||
from __future__ import unicode_literals
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import contextlib
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
@ -53,7 +54,7 @@ def main():
 | 
				
			|||||||
        command = TopLevelCommand()
 | 
					        command = TopLevelCommand()
 | 
				
			||||||
        command.sys_dispatch()
 | 
					        command.sys_dispatch()
 | 
				
			||||||
    except KeyboardInterrupt:
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
        log.error("\nAborting.")
 | 
					        log.error("Aborting.")
 | 
				
			||||||
        sys.exit(1)
 | 
					        sys.exit(1)
 | 
				
			||||||
    except (UserError, NoSuchService, ConfigurationError) as e:
 | 
					    except (UserError, NoSuchService, ConfigurationError) as e:
 | 
				
			||||||
        log.error(e.msg)
 | 
					        log.error(e.msg)
 | 
				
			||||||
@ -629,18 +630,20 @@ class TopLevelCommand(DocoptCommand):
 | 
				
			|||||||
        if detached and cascade_stop:
 | 
					        if detached and cascade_stop:
 | 
				
			||||||
            raise UserError("--abort-on-container-exit and -d cannot be combined.")
 | 
					            raise UserError("--abort-on-container-exit and -d cannot be combined.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        to_attach = project.up(
 | 
					        with up_shutdown_context(project, service_names, timeout, detached):
 | 
				
			||||||
            service_names=service_names,
 | 
					            to_attach = project.up(
 | 
				
			||||||
            start_deps=start_deps,
 | 
					                service_names=service_names,
 | 
				
			||||||
            strategy=convergence_strategy_from_opts(options),
 | 
					                start_deps=start_deps,
 | 
				
			||||||
            do_build=not options['--no-build'],
 | 
					                strategy=convergence_strategy_from_opts(options),
 | 
				
			||||||
            timeout=timeout,
 | 
					                do_build=not options['--no-build'],
 | 
				
			||||||
            detached=detached
 | 
					                timeout=timeout,
 | 
				
			||||||
        )
 | 
					                detached=detached)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not detached:
 | 
					            if detached:
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
            log_printer = build_log_printer(to_attach, service_names, monochrome, cascade_stop)
 | 
					            log_printer = build_log_printer(to_attach, service_names, monochrome, cascade_stop)
 | 
				
			||||||
            attach_to_logs(project, log_printer, service_names, timeout)
 | 
					            print("Attaching to", list_containers(log_printer.containers))
 | 
				
			||||||
 | 
					            log_printer.run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def version(self, project, options):
 | 
					    def version(self, project, options):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -740,13 +743,16 @@ def build_log_printer(containers, service_names, monochrome, cascade_stop):
 | 
				
			|||||||
    return LogPrinter(containers, monochrome=monochrome, cascade_stop=cascade_stop)
 | 
					    return LogPrinter(containers, monochrome=monochrome, cascade_stop=cascade_stop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def attach_to_logs(project, log_printer, service_names, timeout):
 | 
					@contextlib.contextmanager
 | 
				
			||||||
    print("Attaching to", list_containers(log_printer.containers))
 | 
					def up_shutdown_context(project, service_names, timeout, detached):
 | 
				
			||||||
    signals.set_signal_handler_to_shutdown()
 | 
					    if detached:
 | 
				
			||||||
 | 
					        yield
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    signals.set_signal_handler_to_shutdown()
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            log_printer.run()
 | 
					            yield
 | 
				
			||||||
        except signals.ShutdownException:
 | 
					        except signals.ShutdownException:
 | 
				
			||||||
            print("Gracefully stopping... (press Ctrl+C again to force)")
 | 
					            print("Gracefully stopping... (press Ctrl+C again to force)")
 | 
				
			||||||
            project.stop(service_names=service_names, timeout=timeout)
 | 
					            project.stop(service_names=service_names, timeout=timeout)
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,8 @@ def start_process(base_dir, options):
 | 
				
			|||||||
def wait_on_process(proc, returncode=0):
 | 
					def wait_on_process(proc, returncode=0):
 | 
				
			||||||
    stdout, stderr = proc.communicate()
 | 
					    stdout, stderr = proc.communicate()
 | 
				
			||||||
    if proc.returncode != returncode:
 | 
					    if proc.returncode != returncode:
 | 
				
			||||||
        print(stderr.decode('utf-8'))
 | 
					        print("Stderr: {}".format(stderr))
 | 
				
			||||||
 | 
					        print("Stdout: {}".format(stdout))
 | 
				
			||||||
        assert proc.returncode == returncode
 | 
					        assert proc.returncode == returncode
 | 
				
			||||||
    return ProcessResult(stdout.decode('utf-8'), stderr.decode('utf-8'))
 | 
					    return ProcessResult(stdout.decode('utf-8'), stderr.decode('utf-8'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,7 +82,6 @@ class ContainerStateCondition(object):
 | 
				
			|||||||
        self.name = name
 | 
					        self.name = name
 | 
				
			||||||
        self.running = running
 | 
					        self.running = running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # State.Running == true
 | 
					 | 
				
			||||||
    def __call__(self):
 | 
					    def __call__(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            container = self.client.inspect_container(self.name)
 | 
					            container = self.client.inspect_container(self.name)
 | 
				
			||||||
@ -707,6 +707,17 @@ class CLITestCase(DockerClientTestCase):
 | 
				
			|||||||
        os.kill(proc.pid, signal.SIGTERM)
 | 
					        os.kill(proc.pid, signal.SIGTERM)
 | 
				
			||||||
        wait_on_condition(ContainerCountCondition(self.project, 0))
 | 
					        wait_on_condition(ContainerCountCondition(self.project, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @v2_only()
 | 
				
			||||||
 | 
					    def test_up_handles_force_shutdown(self):
 | 
				
			||||||
 | 
					        self.base_dir = 'tests/fixtures/sleeps-composefile'
 | 
				
			||||||
 | 
					        proc = start_process(self.base_dir, ['up', '-t', '200'])
 | 
				
			||||||
 | 
					        wait_on_condition(ContainerCountCondition(self.project, 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        os.kill(proc.pid, signal.SIGTERM)
 | 
				
			||||||
 | 
					        time.sleep(0.1)
 | 
				
			||||||
 | 
					        os.kill(proc.pid, signal.SIGTERM)
 | 
				
			||||||
 | 
					        wait_on_condition(ContainerCountCondition(self.project, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_run_service_without_links(self):
 | 
					    def test_run_service_without_links(self):
 | 
				
			||||||
        self.base_dir = 'tests/fixtures/links-composefile'
 | 
					        self.base_dir = 'tests/fixtures/links-composefile'
 | 
				
			||||||
        self.dispatch(['run', 'console', '/bin/true'])
 | 
					        self.dispatch(['run', 'console', '/bin/true'])
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								tests/fixtures/sleeps-composefile/docker-compose.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/fixtures/sleeps-composefile/docker-compose.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					version: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  simple:
 | 
				
			||||||
 | 
					    image: busybox:latest
 | 
				
			||||||
 | 
					    command: sleep 200
 | 
				
			||||||
 | 
					  another:
 | 
				
			||||||
 | 
					    image: busybox:latest
 | 
				
			||||||
 | 
					    command: sleep 200
 | 
				
			||||||
@ -6,12 +6,9 @@ import logging
 | 
				
			|||||||
from compose import container
 | 
					from compose import container
 | 
				
			||||||
from compose.cli.errors import UserError
 | 
					from compose.cli.errors import UserError
 | 
				
			||||||
from compose.cli.formatter import ConsoleWarningFormatter
 | 
					from compose.cli.formatter import ConsoleWarningFormatter
 | 
				
			||||||
from compose.cli.log_printer import LogPrinter
 | 
					 | 
				
			||||||
from compose.cli.main import attach_to_logs
 | 
					 | 
				
			||||||
from compose.cli.main import build_log_printer
 | 
					from compose.cli.main import build_log_printer
 | 
				
			||||||
from compose.cli.main import convergence_strategy_from_opts
 | 
					from compose.cli.main import convergence_strategy_from_opts
 | 
				
			||||||
from compose.cli.main import setup_console_handler
 | 
					from compose.cli.main import setup_console_handler
 | 
				
			||||||
from compose.project import Project
 | 
					 | 
				
			||||||
from compose.service import ConvergenceStrategy
 | 
					from compose.service import ConvergenceStrategy
 | 
				
			||||||
from tests import mock
 | 
					from tests import mock
 | 
				
			||||||
from tests import unittest
 | 
					from tests import unittest
 | 
				
			||||||
@ -49,21 +46,6 @@ class CLIMainTestCase(unittest.TestCase):
 | 
				
			|||||||
        log_printer = build_log_printer(containers, service_names, True, False)
 | 
					        log_printer = build_log_printer(containers, service_names, True, False)
 | 
				
			||||||
        self.assertEqual(log_printer.containers, containers)
 | 
					        self.assertEqual(log_printer.containers, containers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_attach_to_logs(self):
 | 
					 | 
				
			||||||
        project = mock.create_autospec(Project)
 | 
					 | 
				
			||||||
        log_printer = mock.create_autospec(LogPrinter, containers=[])
 | 
					 | 
				
			||||||
        service_names = ['web', 'db']
 | 
					 | 
				
			||||||
        timeout = 12
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        with mock.patch('compose.cli.main.signals.signal', autospec=True) as mock_signal:
 | 
					 | 
				
			||||||
            attach_to_logs(project, log_printer, service_names, timeout)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert mock_signal.signal.mock_calls == [
 | 
					 | 
				
			||||||
            mock.call(mock_signal.SIGINT, mock.ANY),
 | 
					 | 
				
			||||||
            mock.call(mock_signal.SIGTERM, mock.ANY),
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        log_printer.run.assert_called_once_with()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SetupConsoleHandlerTestCase(unittest.TestCase):
 | 
					class SetupConsoleHandlerTestCase(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user