From 963b004749099e904ce706c80d53c3a89f831602 Mon Sep 17 00:00:00 2001 From: John Harris Date: Thu, 4 Jan 2018 10:14:21 -0800 Subject: [PATCH] Add COMPOSE_IGNORE_ORPHANS Signed-off-by: John Harris --- compose/cli/main.py | 20 +++++++++++++++++++- compose/project.py | 15 ++++++++++++--- tests/acceptance/cli_test.py | 6 ++++++ tests/integration/project_test.py | 25 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index 308ac5bb2..81f1e93cd 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -377,9 +377,20 @@ class TopLevelCommand(object): -t, --timeout TIMEOUT Specify a shutdown timeout in seconds. (default: 10) """ + environment = Environment.from_env_file(self.project_dir) + ignore_orphans = environment.get_boolean('COMPOSE_IGNORE_ORPHANS') + + if ignore_orphans and options['--remove-orphans']: + raise UserError("COMPOSE_IGNORE_ORPHANS and --remove-orphans cannot be combined.") + image_type = image_type_from_opt('--rmi', options['--rmi']) timeout = timeout_from_opts(options) - self.project.down(image_type, options['--volumes'], options['--remove-orphans'], timeout=timeout) + self.project.down( + image_type, + options['--volumes'], + options['--remove-orphans'], + timeout=timeout, + ignore_orphans=ignore_orphans) def events(self, options): """ @@ -941,6 +952,12 @@ class TopLevelCommand(object): if detached and timeout: raise UserError("-d and --timeout cannot be combined.") + environment = Environment.from_env_file(self.project_dir) + ignore_orphans = environment.get_boolean('COMPOSE_IGNORE_ORPHANS') + + if ignore_orphans and remove_orphans: + raise UserError("COMPOSE_IGNORE_ORPHANS and --remove-orphans cannot be combined.") + if no_start: for excluded in ['-d', '--abort-on-container-exit', '--exit-code-from']: if options.get(excluded): @@ -955,6 +972,7 @@ class TopLevelCommand(object): timeout=timeout, detached=detached, remove_orphans=remove_orphans, + ignore_orphans=ignore_orphans, scale_override=parse_scale_args(options['--scale']), start=not no_start ) diff --git a/compose/project.py b/compose/project.py index 11ee4a0b7..6683a3cba 100644 --- a/compose/project.py +++ b/compose/project.py @@ -330,9 +330,16 @@ class Project(object): service_names, stopped=True, one_off=one_off ), options) - def down(self, remove_image_type, include_volumes, remove_orphans=False, timeout=None): + def down( + self, + remove_image_type, + include_volumes, + remove_orphans=False, + timeout=None, + ignore_orphans=False): self.stop(one_off=OneOffFilter.include, timeout=timeout) - self.find_orphan_containers(remove_orphans) + if not ignore_orphans: + self.find_orphan_containers(remove_orphans) self.remove_stopped(v=include_volumes, one_off=OneOffFilter.include) self.networks.remove() @@ -432,6 +439,7 @@ class Project(object): timeout=None, detached=False, remove_orphans=False, + ignore_orphans=False, scale_override=None, rescale=True, start=True): @@ -439,7 +447,8 @@ class Project(object): warn_for_swarm_mode(self.client) self.initialize() - self.find_orphan_containers(remove_orphans) + if not ignore_orphans: + self.find_orphan_containers(remove_orphans) if scale_override is None: scale_override = {} diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index c06471650..f801704d7 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -1337,6 +1337,12 @@ class CLITestCase(DockerClientTestCase): result = self.dispatch(['up', '-d', '-t', '1'], returncode=1) assert "-d and --timeout cannot be combined." in result.stderr + @mock.patch.dict(os.environ) + def test_up_with_ignore_remove_orphans(self): + os.environ["COMPOSE_IGNORE_ORPHANS"] = "True" + result = self.dispatch(['up', '-d', '--remove-orphans'], returncode=1) + assert "COMPOSE_IGNORE_ORPHANS and --remove-orphans cannot be combined." in result.stderr + def test_up_handles_sigint(self): proc = start_process(self.base_dir, ['up', '-t', '2']) wait_on_condition(ContainerCountCondition(self.project, 2)) diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index 3180c1b9b..a9ca3be61 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -1633,6 +1633,31 @@ class ProjectTest(DockerClientTestCase): if ctnr.labels.get(LABEL_SERVICE) == 'service1' ]) == 0 + def test_project_up_ignore_orphans(self): + config_dict = { + 'service1': { + 'image': 'busybox:latest', + 'command': 'top', + } + } + + config_data = load_config(config_dict) + project = Project.from_config( + name='composetest', config_data=config_data, client=self.client + ) + project.up() + config_dict['service2'] = config_dict['service1'] + del config_dict['service1'] + + config_data = load_config(config_dict) + project = Project.from_config( + name='composetest', config_data=config_data, client=self.client + ) + with mock.patch('compose.project.log') as mock_log: + project.up(ignore_orphans=True) + + mock_log.warning.assert_not_called() + @v2_1_only() def test_project_up_healthy_dependency(self): config_dict = {