From b318585f3c5213cb76c6cb803257a62cdefb85aa Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 13 Oct 2014 19:14:46 +0100 Subject: [PATCH] TLS support, with same env vars as docker client Thanks to @jkingyens for the bulk of the work. Signed-off-by: Aanand Prasad --- fig/cli/command.py | 10 ++++++---- fig/cli/docker_client.py | 34 ++++++++++++++++++++++++++++++++++ fig/cli/utils.py | 4 ---- tests/integration/testcases.py | 5 ++--- 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 fig/cli/docker_client.py diff --git a/fig/cli/command.py b/fig/cli/command.py index c65d72712..743c96e93 100644 --- a/fig/cli/command.py +++ b/fig/cli/command.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals from __future__ import absolute_import -from docker import Client -from requests.exceptions import ConnectionError +from requests.exceptions import ConnectionError, SSLError import errno import logging import os @@ -12,7 +11,8 @@ import six from ..project import Project from ..service import ConfigError from .docopt_command import DocoptCommand -from .utils import docker_url, call_silently, is_mac, is_ubuntu +from .utils import call_silently, is_mac, is_ubuntu +from .docker_client import docker_client from . import verbose_proxy from . import errors from .. import __version__ @@ -26,6 +26,8 @@ class Command(DocoptCommand): def dispatch(self, *args, **kwargs): try: super(Command, self).dispatch(*args, **kwargs) + except SSLError, e: + raise errors.UserError('SSL error: %s' % e) except ConnectionError: if call_silently(['which', 'docker']) != 0: if is_mac(): @@ -49,7 +51,7 @@ class Command(DocoptCommand): handler(project, command_options) def get_client(self, verbose=False): - client = Client(docker_url()) + client = docker_client() if verbose: version_info = six.iteritems(client.version()) log.info("Fig version %s", __version__) diff --git a/fig/cli/docker_client.py b/fig/cli/docker_client.py new file mode 100644 index 000000000..73a183225 --- /dev/null +++ b/fig/cli/docker_client.py @@ -0,0 +1,34 @@ +from docker import Client +from docker import tls +import ssl +import os + + +def docker_client(): + """ + Returns a docker-py client configured using environment variables + according to the same logic as the official Docker client. + """ + cert_path = os.environ.get('DOCKER_CERT_PATH', '') + if cert_path == '': + cert_path = os.path.join(os.environ.get('HOME'), '.docker') + + base_url = os.environ.get('DOCKER_HOST') + tls_config = None + + if os.environ.get('DOCKER_TLS_VERIFY', '') != '': + parts = base_url.split('://', 1) + base_url = '%s://%s' % ('https', parts[1]) + + 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') + + tls_config = tls.TLSConfig( + ssl_version=ssl.PROTOCOL_TLSv1, + verify=True, + assert_hostname=False, + client_cert=client_cert, + ca_cert=ca_cert, + ) + + return Client(base_url=base_url, tls=tls_config) diff --git a/fig/cli/utils.py b/fig/cli/utils.py index af16e2449..d64eef4bc 100644 --- a/fig/cli/utils.py +++ b/fig/cli/utils.py @@ -62,10 +62,6 @@ def mkdir(path, permissions=0o700): return path -def docker_url(): - return os.environ.get('DOCKER_HOST') - - def split_buffer(reader, separator): """ Given a generator which yields strings and a separator string, diff --git a/tests/integration/testcases.py b/tests/integration/testcases.py index 37d74b589..9e65b9044 100644 --- a/tests/integration/testcases.py +++ b/tests/integration/testcases.py @@ -1,8 +1,7 @@ from __future__ import unicode_literals from __future__ import absolute_import -from docker import Client from fig.service import Service -from fig.cli.utils import docker_url +from fig.cli.docker_client import docker_client from fig.progress_stream import stream_output from .. import unittest @@ -10,7 +9,7 @@ from .. import unittest class DockerClientTestCase(unittest.TestCase): @classmethod def setUpClass(cls): - cls.client = Client(docker_url()) + cls.client = docker_client() def setUp(self): for c in self.client.containers(all=True):