Repository: docker/docker-py

Issue:
swarm mode create service does not support volumn bind type?
hi all, i use docker py create service catch some error. my docker py version is    
```
root@node-19:~# pip freeze | grep docker
docker==2.0.0
docker-compose==1.9.0
docker-pycreds==0.2.1
dockerpty==0.4.1
```

my docker version is       
```
Client:
 Version:      1.12.5
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   7392c3b
 Built:        Fri Dec 16 02:30:42 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.12.5
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   7392c3b
 Built:        Fri Dec 16 02:30:42 2016
 OS/Arch:      linux/amd64
```

my use docker py create service code is        
```python
import docker
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
client.services.create(name="bab", command=["sleep", "30000"], image="busybox", mounts=["/data:/data:rw"])
```
look my create service code i just wand to create service and volumn local host dir to container, and dir path is absolute path. but i got some error like this
 `
invalid volume mount source, must not be an absolute path: /data`

so i check docker py code find this     
```python
    def __init__(self, target, source, type='volume', read_only=False,
                 propagation=None, no_copy=False, labels=None,
                 driver_config=None):
        self['Target'] = target
        self['Source'] = source
        if type not in ('bind', 'volume'):
            raise errors.DockerError(
                'Only acceptable mount types are `bind` and `volume`.'
            )
        self['Type'] = type
```
so i want to know what can i create service with volumn bind type . i need help.


Discussion:
@shin- 
Sorry, that's a bug on our end. Here's a workaround you can use in the meantime:

```python
import docker
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
mounts = [
  docker.types.Mount(target='/data', source='/data', type='bind'),
]
client.services.create(
  name="bab", command=["sleep", "30000"], image="busybox", mounts=mounts
)
```

HTH
@shin- ok  i already use in your method。thanks。i have another question. just i want to scale service 1 to 5. but i can found in docker-py, i use update_service method can not do yet。what can i do。

Resolving patch:
diff --git a/docker/types/services.py b/docker/types/services.py
--- a/docker/types/services.py
+++ b/docker/types/services.py
@@ -1,6 +1,7 @@
 import six
 
 from .. import errors
+from ..constants import IS_WINDOWS_PLATFORM
 from ..utils import format_environment, split_command
 
 
@@ -175,8 +176,17 @@ def parse_mount_string(cls, string):
         else:
             target = parts[1]
             source = parts[0]
+            mount_type = 'volume'
+            if source.startswith('/') or (
+                IS_WINDOWS_PLATFORM and source[0].isalpha() and
+                source[1] == ':'
+            ):
+                # FIXME: That windows condition will fail earlier since we
+                # split on ':'. We should look into doing a smarter split
+                # if we detect we are on Windows.
+                mount_type = 'bind'
             read_only = not (len(parts) == 2 or parts[2] == 'rw')
-            return cls(target, source, read_only=read_only)
+            return cls(target, source, read_only=read_only, type=mount_type)
 
 
 class Resources(dict):


Test patch:
diff --git a/tests/unit/dockertypes_test.py b/tests/unit/dockertypes_test.py
--- a/tests/unit/dockertypes_test.py
+++ b/tests/unit/dockertypes_test.py
@@ -10,6 +10,11 @@
     EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Mount, Ulimit,
 )
 
+try:
+    from unittest import mock
+except:
+    import mock
+
 
 def create_host_config(*args, **kwargs):
     return HostConfig(*args, **kwargs)
@@ -258,28 +263,48 @@ def test_create_ipam_config(self):
 class TestMounts(unittest.TestCase):
     def test_parse_mount_string_ro(self):
         mount = Mount.parse_mount_string("/foo/bar:/baz:ro")
-        self.assertEqual(mount['Source'], "/foo/bar")
-        self.assertEqual(mount['Target'], "/baz")
-        self.assertEqual(mount['ReadOnly'], True)
+        assert mount['Source'] == "/foo/bar"
+        assert mount['Target'] == "/baz"
+        assert mount['ReadOnly'] is True
 
     def test_parse_mount_string_rw(self):
         mount = Mount.parse_mount_string("/foo/bar:/baz:rw")
-        self.assertEqual(mount['Source'], "/foo/bar")
-        self.assertEqual(mount['Target'], "/baz")
-        self.assertEqual(mount['ReadOnly'], False)
+        assert mount['Source'] == "/foo/bar"
+        assert mount['Target'] == "/baz"
+        assert not mount['ReadOnly']
 
     def test_parse_mount_string_short_form(self):
         mount = Mount.parse_mount_string("/foo/bar:/baz")
-        self.assertEqual(mount['Source'], "/foo/bar")
-        self.assertEqual(mount['Target'], "/baz")
-        self.assertEqual(mount['ReadOnly'], False)
+        assert mount['Source'] == "/foo/bar"
+        assert mount['Target'] == "/baz"
+        assert not mount['ReadOnly']
 
     def test_parse_mount_string_no_source(self):
         mount = Mount.parse_mount_string("foo/bar")
-        self.assertEqual(mount['Source'], None)
-        self.assertEqual(mount['Target'], "foo/bar")
-        self.assertEqual(mount['ReadOnly'], False)
+        assert mount['Source'] is None
+        assert mount['Target'] == "foo/bar"
+        assert not mount['ReadOnly']
 
     def test_parse_mount_string_invalid(self):
         with pytest.raises(InvalidArgument):
             Mount.parse_mount_string("foo:bar:baz:rw")
+
+    def test_parse_mount_named_volume(self):
+        mount = Mount.parse_mount_string("foobar:/baz")
+        assert mount['Source'] == 'foobar'
+        assert mount['Target'] == '/baz'
+        assert mount['Type'] == 'volume'
+
+    def test_parse_mount_bind(self):
+        mount = Mount.parse_mount_string('/foo/bar:/baz')
+        assert mount['Source'] == "/foo/bar"
+        assert mount['Target'] == "/baz"
+        assert mount['Type'] == 'bind'
+
+    @pytest.mark.xfail
+    def test_parse_mount_bind_windows(self):
+        with mock.patch('docker.types.services.IS_WINDOWS_PLATFORM', True):
+            mount = Mount.parse_mount_string('C:/foo/bar:/baz')
+        assert mount['Source'] == "C:/foo/bar"
+        assert mount['Target'] == "/baz"
+        assert mount['Type'] == 'bind'