Repository: XD-embedded/xd-docker

Issue:
client.container_start()
Client API command to start a container.

Discussion:

Patch:
diff --git a/CHANGELOG b/CHANGELOG
index 7fdaf1f..6051fc5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 0.2.0 (in development)
 ----------------------
 * Add container_remove() method.
+* Add container_start() method.
 
 0.1.0 (2016-08-13)
 ------------------
diff --git a/xd/docker/client.py b/xd/docker/client.py
index 4e16fa9..049e530 100644
--- a/xd/docker/client.py
+++ b/xd/docker/client.py
@@ -458,3 +458,30 @@ class DockerClient(object):
 
         self._delete('/containers/' + id_or_name, params=query_params)
         return
+
+    def container_start(self, container: Union[Container, ContainerName, str]):
+        """Start a container.
+
+        Arguments:
+          container: The container to start (id or name).
+
+        Returns:
+          True if container was started.
+          False if container was already started.
+        """
+
+        # Handle convenience argument types
+        if isinstance(container, str):
+            id_or_name = container
+        elif isinstance(container, ContainerName):
+            id_or_name = container.name
+        else:
+            id_or_name = container.id or container.name
+
+        try:
+            self._post('/containers/{}/start'.format(id_or_name))
+        except HTTPError as e:
+            if e.code == 304:
+                return False
+            raise e
+        return True


Test patch:
diff --git a/tests/integration/container_start_test.py b/tests/integration/container_start_test.py
new file mode 100644
index 0000000..7321187
--- /dev/null
+++ b/tests/integration/container_start_test.py
@@ -0,0 +1,22 @@
+import pytest
+import os
+import re
+
+from xd.docker.client import *
+
+
+def test_ok(docker, stdout):
+    os.system("docker create --name xd-docker-test busybox:latest echo test")
+    assert docker.container_start('xd-docker-test') == True
+
+    
+def test_no_such_container(docker, stdout):
+    with pytest.raises(ClientError) as clienterror:
+        with stdout.redirect():
+            docker.container_start('xd-docker-test')
+    assert clienterror.value.code == 404
+
+    
+def test_container_running(docker, stdout):
+    os.system("docker run -d --name xd-docker-test busybox:latest sleep 600")
+    assert docker.container_start('xd-docker-test') == False
diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py
index 55c1443..59bddf6 100644
--- a/tests/unit/client_test.py
+++ b/tests/unit/client_test.py
@@ -1174,3 +1174,57 @@ class container_remove_tests(ContextClientTestCase):
         self.client.container_remove('foobar', volumes=False)
         params = delete_mock.call_args[1]['params']
         assert 'v' in params and not params['v']
+
+
+class container_start_tests(ContextClientTestCase):
+
+    @mock.patch('requests.post')
+    def test_str(self, post_mock):
+        post_mock.return_value = requests_mock.Response("OK", 204)
+        assert self.client.container_start('foobar')
+        assert not post_mock.call_args[1]['params']
+        assert post_mock.call_args[0][0].endswith('/containers/foobar/start')
+
+    @mock.patch('requests.post')
+    def test_containername(self, post_mock):
+        post_mock.return_value = requests_mock.Response("OK", 204)
+        assert self.client.container_start(ContainerName('foobar'))
+        assert not post_mock.call_args[1]['params']
+        assert post_mock.call_args[0][0].endswith('/containers/foobar/start')
+
+    @mock.patch('requests.post')
+    def test_container_with_name(self, post_mock):
+        post_mock.return_value = requests_mock.Response("OK", 204)
+        assert self.client.container_start(
+            Container(self.client, name='foobar'))
+        assert post_mock.call_args[0][0].endswith('/containers/foobar/start')
+
+    @mock.patch('requests.post')
+    def test_container_with_id(self, post_mock):
+        post_mock.return_value = requests_mock.Response("OK", 204)
+        assert self.client.container_start(
+            Container(self.client, id='dfeb03b02b41'))
+        assert post_mock.call_args[0][0].endswith(
+            '/containers/dfeb03b02b41/start')
+
+    @mock.patch('requests.post')
+    def test_container_with_id_and_name(self, post_mock):
+        post_mock.return_value = requests_mock.Response("OK", 204)
+        assert self.client.container_start(
+            Container(self.client, id='dfeb03b02b41', name='foobar'))
+        assert post_mock.call_args[0][0].endswith(
+            '/containers/dfeb03b02b41/start')
+
+    @mock.patch('requests.post')
+    def test_already_running(self, post_mock):
+        post_mock.return_value = requests_mock.Response(
+            "Container already started", 304)
+        assert not self.client.container_start('foobar')
+
+    @mock.patch('requests.post')
+    def test_no_such_container(self, post_mock):
+        post_mock.return_value = requests_mock.Response(
+            "No such container", 404)
+        with pytest.raises(ClientError) as clienterror:
+            self.client.container_start('foobar')
+        assert clienterror.value.code == 404