Skip to content

Commit e6af420

Browse files
committed
Merge pull request #304 from shreyu86/dev-restart-policy
Restart Policy for Containers Implemented fixes #302
2 parents c76fd8d + 7479d4c commit e6af420

File tree

4 files changed

+81
-11
lines changed

4 files changed

+81
-11
lines changed

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ Identical to the `docker search` command.
232232
```python
233233
c.start(container, binds=None, port_bindings=None, lxc_conf=None,
234234
publish_all_ports=False, links=None, privileged=False,
235-
dns=None, dns_search=None, volumes_from=None, network_mode=None)
235+
dns=None, dns_search=None, volumes_from=None, network_mode=None, restart_policy=None)
236236
```
237237

238238
Similar to the `docker start` command, but doesn't support attach
@@ -258,6 +258,27 @@ docker bridge, 'none': no networking for this container, 'container:[name|id]':
258258
reuses another container network stack), 'host': use the host network stack
259259
inside the container.
260260

261+
`restart_policy` is available since v1.2.0 and sets the RestartPolicy for how a container should or should not be
262+
restarted on exit. By default the policy is set to no meaning do not restart the container when it exits.
263+
The user may specify the restart policy as a dictionary for example:
264+
for example:
265+
```
266+
{
267+
"MaximumRetryCount": 0,
268+
"Name": "always"
269+
}
270+
```
271+
for always restarting the container on exit or can specify to restart the container to restart on failure and can limit
272+
number of restarts.
273+
for example:
274+
```
275+
{
276+
"MaximumRetryCount": 5,
277+
"Name": "on-failure"
278+
}
279+
```
280+
281+
261282
```python
262283
c.stop(container, timeout=10)
263284
```

docker/client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,8 @@ def search(self, term):
806806

807807
def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
808808
publish_all_ports=False, links=None, privileged=False,
809-
dns=None, dns_search=None, volumes_from=None, network_mode=None):
809+
dns=None, dns_search=None, volumes_from=None, network_mode=None,
810+
restart_policy=None):
810811
if isinstance(container, dict):
811812
container = container.get('Id')
812813

@@ -859,13 +860,15 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
859860
if volumes_from is not None:
860861
warnings.warn(warning_message.format('volumes_from'),
861862
DeprecationWarning)
862-
863863
if dns_search:
864864
start_config['DnsSearch'] = dns_search
865865

866866
if network_mode:
867867
start_config['NetworkMode'] = network_mode
868868

869+
if restart_policy:
870+
start_config['RestartPolicy'] = restart_policy
871+
869872
url = self._url("/containers/{0}/start".format(container))
870873
res = self._post_json(url, data=start_config)
871874
self._raise_for_status(res)

tests/integration_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,23 @@ def runTest(self):
612612
self.assertIn('{0}_NAME='.format(link_env_prefix2), logs)
613613
self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix2), logs)
614614

615+
616+
class TestRestartingContainer(BaseTestCase):
617+
def runTest(self):
618+
container = self.client.create_container('busybox', ['false'])
619+
id = container['Id']
620+
self.client.start(id, restart_policy={
621+
{
622+
"Name": "on-failure",
623+
"MaximumRetryCount": 1
624+
}
625+
})
626+
self.client.wait(id)
627+
self.client.remove_container(id)
628+
containers = self.client.containers(all=True)
629+
res = [x for x in containers if 'Id' in x and x['Id'].startswith(id)]
630+
self.assertEqual(len(res), 0)
631+
615632
#################
616633
# LINKS TESTS #
617634
#################

tests/test.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2013 dotCloud inc.
22

3-
# Licensed under the Apache License, Version 2.0 (the "License");
4-
# you may not use this file except in compliance with the License.
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
55
# You may obtain a copy of the License at
66

77
# http://www.apache.org/licenses/LICENSE-2.0
@@ -60,6 +60,7 @@ def fake_resp(url, data=None, **kwargs):
6060
status_code, content = fake_api.fake_responses[url]()
6161
return response(status_code=status_code, content=content)
6262

63+
6364
fake_request = mock.Mock(side_effect=fake_resp)
6465
url_prefix = 'http+unix://var/run/docker.sock/v{0}/'.format(
6566
docker.client.DEFAULT_DOCKER_API_VERSION)
@@ -616,7 +617,7 @@ def test_start_container_with_binds_rw(self):
616617
mount_origin = '/tmp'
617618
self.client.start(fake_api.FAKE_CONTAINER_ID,
618619
binds={mount_origin: {
619-
"bind": mount_dest, "ro": False}})
620+
"bind": mount_dest, "ro": False}})
620621
except Exception as e:
621622
self.fail('Command should not raise exception: {0}'.format(e))
622623

@@ -812,6 +813,34 @@ def test_start_container_with_dict_instead_of_id(self):
812813
docker.client.DEFAULT_TIMEOUT_SECONDS
813814
)
814815

816+
def test_start_container_with_restart_policy(self):
817+
try:
818+
self.client.start(fake_api.FAKE_CONTAINER_ID,
819+
restart_policy={
820+
"Name": "always",
821+
"MaximumRetryCount": 0
822+
})
823+
except Exception as e:
824+
self.fail('Command should not raise exception: {0}'.format(e))
825+
args = fake_request.call_args
826+
self.assertEqual(
827+
args[0][0],
828+
url_prefix + 'containers/3cc2351ab11b/start'
829+
)
830+
self.assertEqual(
831+
json.loads(args[1]['data']),
832+
{"PublishAllPorts": False, "Privileged": False,
833+
"RestartPolicy": {"MaximumRetryCount": 0, "Name": "always"}}
834+
)
835+
self.assertEqual(
836+
args[1]['headers'],
837+
{'Content-Type': 'application/json'}
838+
)
839+
self.assertEqual(
840+
args[1]['timeout'],
841+
docker.client.DEFAULT_TIMEOUT_SECONDS
842+
)
843+
815844
def test_resize_container(self):
816845
try:
817846
self.client.resize(
@@ -1536,11 +1565,11 @@ def test_tar_with_excludes(self):
15361565
f.write("content")
15371566

15381567
for exclude, names in (
1539-
(['*.py'], ['bar/a.txt', 'bar/other.png',
1540-
'test/foo/a.txt', 'test/foo/other.png']),
1541-
(['*.png', 'bar'], ['test/foo/a.txt', 'test/foo/b.py']),
1542-
(['test/foo', 'a.txt'], ['bar/a.txt', 'bar/b.py',
1543-
'bar/other.png']),
1568+
(['*.py'], ['bar/a.txt', 'bar/other.png',
1569+
'test/foo/a.txt', 'test/foo/other.png']),
1570+
(['*.png', 'bar'], ['test/foo/a.txt', 'test/foo/b.py']),
1571+
(['test/foo', 'a.txt'], ['bar/a.txt', 'bar/b.py',
1572+
'bar/other.png']),
15441573
):
15451574
archive = docker.utils.tar(base, exclude=exclude)
15461575
tar = tarfile.open(fileobj=archive)

0 commit comments

Comments
 (0)