diff --git a/compose/config/config.py b/compose/config/config.py index 918946b3965..cdd35e12172 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -62,6 +62,7 @@ 'restart', 'security_opt', 'stdin_open', + 'stop_signal', 'tty', 'user', 'volume_driver', diff --git a/compose/config/service_schema_v1.json b/compose/config/service_schema_v1.json index d51c7f731b1..43e7c8b8d93 100644 --- a/compose/config/service_schema_v1.json +++ b/compose/config/service_schema_v1.json @@ -94,6 +94,7 @@ "restart": {"type": "string"}, "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "stdin_open": {"type": "boolean"}, + "stop_signal": {"type": "string"}, "tty": {"type": "boolean"}, "ulimits": { "type": "object", diff --git a/compose/config/service_schema_v2.json b/compose/config/service_schema_v2.json index 47b195fcc03..10331dccf87 100644 --- a/compose/config/service_schema_v2.json +++ b/compose/config/service_schema_v2.json @@ -101,6 +101,7 @@ "restart": {"type": "string"}, "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "stdin_open": {"type": "boolean"}, + "stop_signal": {"type": "string"}, "tty": {"type": "boolean"}, "ulimits": { "type": "object", diff --git a/compose/container.py b/compose/container.py index 5730f224618..2565c8ffc38 100644 --- a/compose/container.py +++ b/compose/container.py @@ -107,6 +107,10 @@ def format_port(private, public): def labels(self): return self.get('Config.Labels') or {} + @property + def stop_signal(self): + return self.get('Config.StopSignal') + @property def log_config(self): return self.get('HostConfig.LogConfig') or None @@ -132,6 +136,10 @@ def human_readable_command(self): def environment(self): return dict(var.split("=", 1) for var in self.get('Config.Env') or []) + @property + def exit_code(self): + return self.get('State.ExitCode') + @property def is_running(self): return self.get('State.Running') diff --git a/requirements.txt b/requirements.txt index 8c6d5f3ad40..313c6b7acc3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ PyYAML==3.11 -docker-py==1.6.0 dockerpty==0.3.4 docopt==0.6.1 enum34==1.0.4 +git+https://github.com/docker/docker-py.git@master#egg=docker-py jsonschema==2.5.1 requests==2.7.0 six==1.7.3 diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index eab4bdae978..90dca298e06 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -717,6 +717,18 @@ def test_stop(self): self.assertEqual(len(service.containers(stopped=True)), 1) self.assertFalse(service.containers(stopped=True)[0].is_running) + def test_stop_signal(self): + self.base_dir = 'tests/fixtures/stop-signal-composefile' + self.dispatch(['up', '-d'], None) + service = self.project.get_service('simple') + self.assertEqual(len(service.containers()), 1) + self.assertTrue(service.containers()[0].is_running) + + self.dispatch(['stop', '-t', '1'], None) + self.assertEqual(len(service.containers(stopped=True)), 1) + self.assertFalse(service.containers(stopped=True)[0].is_running) + self.assertEqual(service.containers(stopped=True)[0].exit_code, 0) + def test_start_no_containers(self): result = self.dispatch(['start'], returncode=1) assert 'No containers to start' in result.stderr diff --git a/tests/fixtures/stop-signal-composefile/docker-compose.yml b/tests/fixtures/stop-signal-composefile/docker-compose.yml new file mode 100644 index 00000000000..04f58aa98aa --- /dev/null +++ b/tests/fixtures/stop-signal-composefile/docker-compose.yml @@ -0,0 +1,10 @@ +simple: + image: busybox:latest + command: + - sh + - '-c' + - | + trap 'exit 0' SIGINT + trap 'exit 1' SIGTERM + while true; do :; done + stop_signal: SIGINT diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index 3eb50942029..4818e47aa8a 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -887,6 +887,12 @@ def test_empty_labels(self): for name in labels_dict: self.assertIn((name, ''), labels) + def test_stop_signal(self): + stop_signal = 'SIGINT' + service = self.create_service('web', stop_signal=stop_signal) + container = create_and_start_container(service) + self.assertEqual(container.stop_signal, stop_signal) + def test_custom_container_name(self): service = self.create_service('web', container_name='my-web-container') self.assertEqual(service.custom_container_name(), 'my-web-container')