Repository: docker/docker-py

Issue:
add api for parsing standard environment variables into a client
With the TLS improvements in docker 1.3.0 creating a `Client` has gotten significantly more complicated. I think the 80% case here is the familiar strings from the environment variables when using the default docker CLI, and it'd be nice if we could easily generate a `Client` object from those options as well.

After upgrading from 1.2.0 to 1.3.0 on the server, I can no longer connect to docker using my `DOCKER_HOST` variable. It is `tcp://192.168.53.102:2356`. However I also have `DOCKER_TLS_VERIFY=1` and `DOCKER_CERT_PATH=$HOME/.boot2docker/certs`. The `docker.utils.parse_host` no longer properly translates `tcp://` correctly because it statically converts to `http://` when this should be `https://`.

Sorry this issue is a little half-baked but it's an initial reaction to upgrading and watching the client be unable to connect to my boot2docker instance any-longer. I'll try to sort it out a bit later unless the issues are obvious to you guys.



Discussion:

Resolving patch:
diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py
--- a/docker/utils/__init__.py
+++ b/docker/utils/__init__.py
@@ -1,4 +1,4 @@
 from .utils import (
     compare_version, convert_port_bindings, convert_volume_binds,
-    mkbuildcontext, ping, tar, parse_repository_tag, parse_host
+    mkbuildcontext, ping, tar, parse_repository_tag, parse_host, kwargs_from_env
 ) # flake8: noqa
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -14,6 +14,7 @@
 
 import io
 import os
+import os.path
 import tarfile
 import tempfile
 from distutils.version import StrictVersion
@@ -23,6 +24,7 @@
 import six
 
 from .. import errors
+from .. import tls
 
 DEFAULT_HTTP_HOST = "127.0.0.1"
 DEFAULT_UNIX_SOCKET = "http+unix://var/run/docker.sock"
@@ -257,3 +259,23 @@ def parse_devices(devices):
                                 "PathInContainer": path_in_container,
                                 "CgroupPermissions": permissions})
     return device_list
+
+
+def kwargs_from_env(ssl_version=None, assert_hostname=None):
+    host = os.environ.get('DOCKER_HOST')
+    cert_path = os.environ.get('DOCKER_CERT_PATH')
+    tls_verify = os.environ.get('DOCKER_TLS_VERIFY')
+
+    params = {}
+    if host:
+        params['base_url'] = (host.replace('tcp://', 'https://')
+                              if tls_verify else host)
+    if tls_verify and cert_path:
+        params['tls'] = tls.TLSConfig(
+            client_cert=(os.path.join(cert_path, 'cert.pem'),
+                         os.path.join(cert_path, 'key.pem')),
+            ca_cert=os.path.join(cert_path, 'ca.pem'),
+            verify=True,
+            ssl_version=ssl_version,
+            assert_hostname=assert_hostname)
+    return params


Test patch:
diff --git a/tests/testdata/certs/ca.pem b/tests/testdata/certs/ca.pem
new file mode 100644
diff --git a/tests/testdata/certs/cert.pem b/tests/testdata/certs/cert.pem
new file mode 100644
diff --git a/tests/testdata/certs/key.pem b/tests/testdata/certs/key.pem
new file mode 100644
diff --git a/tests/utils_test.py b/tests/utils_test.py
--- a/tests/utils_test.py
+++ b/tests/utils_test.py
@@ -1,12 +1,22 @@
 import unittest
 
 from docker.errors import DockerException
-from docker.utils import parse_repository_tag, parse_host
+from docker.utils import parse_repository_tag, parse_host, kwargs_from_env
+from docker.client import Client
+
+import os
+import os.path
 
 
 class UtilsTest(unittest.TestCase):
     longMessage = True
 
+    def setUp(self):
+        self.os_environ = os.environ.copy()
+
+    def tearDown(self):
+        os.environ = self.os_environ
+
     def test_parse_repository_tag(self):
         self.assertEqual(parse_repository_tag("root"),
                          ("root", None))
@@ -53,5 +63,25 @@ def test_parse_host(self):
         for host, expected in valid_hosts.items():
             self.assertEqual(parse_host(host), expected, msg=host)
 
+    def test_kwargs_from_env(self):
+        os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
+                          DOCKER_CERT_PATH=os.path.join(
+                              os.path.dirname(__file__),
+                              'testdata/certs'),
+                          DOCKER_TLS_VERIFY='1')
+        kwargs = kwargs_from_env(assert_hostname=False)
+        self.assertEquals('https://192.168.59.103:2376', kwargs['base_url'])
+        self.assertIn('ca.pem', kwargs['tls'].verify)
+        self.assertIn('cert.pem', kwargs['tls'].cert[0])
+        self.assertIn('key.pem', kwargs['tls'].cert[1])
+        self.assertEquals(False, kwargs['tls'].assert_hostname)
+        try:
+            client = Client(**kwargs)
+            self.assertEquals(kwargs['base_url'], client.base_url)
+            self.assertEquals(kwargs['tls'].verify, client.verify)
+            self.assertEquals(kwargs['tls'].cert, client.cert)
+        except TypeError, e:
+            self.fail(e)
+
 if __name__ == '__main__':
     unittest.main()