1
0

docker_client_test.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import os
  4. import platform
  5. import ssl
  6. import docker
  7. import pytest
  8. import compose
  9. from compose.cli import errors
  10. from compose.cli.docker_client import docker_client
  11. from compose.cli.docker_client import get_tls_version
  12. from compose.cli.docker_client import tls_config_from_options
  13. from compose.config.environment import Environment
  14. from tests import mock
  15. from tests import unittest
  16. class DockerClientTestCase(unittest.TestCase):
  17. def test_docker_client_no_home(self):
  18. with mock.patch.dict(os.environ):
  19. try:
  20. del os.environ['HOME']
  21. except KeyError:
  22. pass
  23. docker_client(os.environ)
  24. @mock.patch.dict(os.environ)
  25. def test_docker_client_with_custom_timeout(self):
  26. os.environ['COMPOSE_HTTP_TIMEOUT'] = '123'
  27. client = docker_client(os.environ)
  28. assert client.timeout == 123
  29. @mock.patch.dict(os.environ)
  30. def test_custom_timeout_error(self):
  31. os.environ['COMPOSE_HTTP_TIMEOUT'] = '123'
  32. client = docker_client(os.environ)
  33. with mock.patch('compose.cli.errors.log') as fake_log:
  34. with pytest.raises(errors.ConnectionError):
  35. with errors.handle_connection_errors(client):
  36. raise errors.RequestsConnectionError(
  37. errors.ReadTimeoutError(None, None, None))
  38. assert fake_log.error.call_count == 1
  39. assert '123' in fake_log.error.call_args[0][0]
  40. with mock.patch('compose.cli.errors.log') as fake_log:
  41. with pytest.raises(errors.ConnectionError):
  42. with errors.handle_connection_errors(client):
  43. raise errors.ReadTimeout()
  44. assert fake_log.error.call_count == 1
  45. assert '123' in fake_log.error.call_args[0][0]
  46. def test_user_agent(self):
  47. client = docker_client(os.environ)
  48. expected = "docker-compose/{0} docker-py/{1} {2}/{3}".format(
  49. compose.__version__,
  50. docker.__version__,
  51. platform.system(),
  52. platform.release()
  53. )
  54. assert client.headers['User-Agent'] == expected
  55. class TLSConfigTestCase(unittest.TestCase):
  56. cert_path = 'tests/fixtures/tls/'
  57. ca_cert = os.path.join(cert_path, 'ca.pem')
  58. client_cert = os.path.join(cert_path, 'cert.pem')
  59. key = os.path.join(cert_path, 'key.pem')
  60. def test_simple_tls(self):
  61. options = {'--tls': True}
  62. result = tls_config_from_options(options)
  63. assert result is True
  64. def test_tls_ca_cert(self):
  65. options = {
  66. '--tlscacert': self.ca_cert, '--tlsverify': True
  67. }
  68. result = tls_config_from_options(options)
  69. assert isinstance(result, docker.tls.TLSConfig)
  70. assert result.ca_cert == options['--tlscacert']
  71. assert result.verify is True
  72. def test_tls_ca_cert_explicit(self):
  73. options = {
  74. '--tlscacert': self.ca_cert, '--tls': True,
  75. '--tlsverify': True
  76. }
  77. result = tls_config_from_options(options)
  78. assert isinstance(result, docker.tls.TLSConfig)
  79. assert result.ca_cert == options['--tlscacert']
  80. assert result.verify is True
  81. def test_tls_client_cert(self):
  82. options = {
  83. '--tlscert': self.client_cert, '--tlskey': self.key
  84. }
  85. result = tls_config_from_options(options)
  86. assert isinstance(result, docker.tls.TLSConfig)
  87. assert result.cert == (options['--tlscert'], options['--tlskey'])
  88. def test_tls_client_cert_explicit(self):
  89. options = {
  90. '--tlscert': self.client_cert, '--tlskey': self.key,
  91. '--tls': True
  92. }
  93. result = tls_config_from_options(options)
  94. assert isinstance(result, docker.tls.TLSConfig)
  95. assert result.cert == (options['--tlscert'], options['--tlskey'])
  96. def test_tls_client_and_ca(self):
  97. options = {
  98. '--tlscert': self.client_cert, '--tlskey': self.key,
  99. '--tlsverify': True, '--tlscacert': self.ca_cert
  100. }
  101. result = tls_config_from_options(options)
  102. assert isinstance(result, docker.tls.TLSConfig)
  103. assert result.cert == (options['--tlscert'], options['--tlskey'])
  104. assert result.ca_cert == options['--tlscacert']
  105. assert result.verify is True
  106. def test_tls_client_and_ca_explicit(self):
  107. options = {
  108. '--tlscert': self.client_cert, '--tlskey': self.key,
  109. '--tlsverify': True, '--tlscacert': self.ca_cert,
  110. '--tls': True
  111. }
  112. result = tls_config_from_options(options)
  113. assert isinstance(result, docker.tls.TLSConfig)
  114. assert result.cert == (options['--tlscert'], options['--tlskey'])
  115. assert result.ca_cert == options['--tlscacert']
  116. assert result.verify is True
  117. def test_tls_client_missing_key(self):
  118. options = {'--tlscert': self.client_cert}
  119. with pytest.raises(docker.errors.TLSParameterError):
  120. tls_config_from_options(options)
  121. options = {'--tlskey': self.key}
  122. with pytest.raises(docker.errors.TLSParameterError):
  123. tls_config_from_options(options)
  124. def test_assert_hostname_explicit_skip(self):
  125. options = {'--tlscacert': self.ca_cert, '--skip-hostname-check': True}
  126. result = tls_config_from_options(options)
  127. assert isinstance(result, docker.tls.TLSConfig)
  128. assert result.assert_hostname is False
  129. def test_tls_client_and_ca_quoted_paths(self):
  130. options = {
  131. '--tlscacert': '"{0}"'.format(self.ca_cert),
  132. '--tlscert': '"{0}"'.format(self.client_cert),
  133. '--tlskey': '"{0}"'.format(self.key),
  134. '--tlsverify': True
  135. }
  136. result = tls_config_from_options(options)
  137. assert isinstance(result, docker.tls.TLSConfig)
  138. assert result.cert == (self.client_cert, self.key)
  139. assert result.ca_cert == self.ca_cert
  140. assert result.verify is True
  141. def test_tls_simple_with_tls_version(self):
  142. tls_version = 'TLSv1'
  143. options = {'--tls': True}
  144. environment = Environment({'COMPOSE_TLS_VERSION': tls_version})
  145. result = tls_config_from_options(options, environment)
  146. assert isinstance(result, docker.tls.TLSConfig)
  147. assert result.ssl_version == ssl.PROTOCOL_TLSv1
  148. def test_tls_mixed_environment_and_flags(self):
  149. options = {'--tls': True, '--tlsverify': False}
  150. environment = Environment({'DOCKER_CERT_PATH': 'tests/fixtures/tls/'})
  151. result = tls_config_from_options(options, environment)
  152. assert isinstance(result, docker.tls.TLSConfig)
  153. assert result.cert == (self.client_cert, self.key)
  154. assert result.ca_cert == self.ca_cert
  155. assert result.verify is False
  156. def test_tls_flags_override_environment(self):
  157. environment = Environment({
  158. 'DOCKER_CERT_PATH': '/completely/wrong/path',
  159. 'DOCKER_TLS_VERIFY': 'false'
  160. })
  161. options = {
  162. '--tlscacert': '"{0}"'.format(self.ca_cert),
  163. '--tlscert': '"{0}"'.format(self.client_cert),
  164. '--tlskey': '"{0}"'.format(self.key),
  165. '--tlsverify': True
  166. }
  167. result = tls_config_from_options(options, environment)
  168. assert isinstance(result, docker.tls.TLSConfig)
  169. assert result.cert == (self.client_cert, self.key)
  170. assert result.ca_cert == self.ca_cert
  171. assert result.verify is True
  172. def test_tls_verify_flag_no_override(self):
  173. environment = Environment({
  174. 'DOCKER_TLS_VERIFY': 'true',
  175. 'COMPOSE_TLS_VERSION': 'TLSv1',
  176. 'DOCKER_CERT_PATH': self.cert_path
  177. })
  178. options = {'--tls': True, '--tlsverify': False}
  179. result = tls_config_from_options(options, environment)
  180. assert isinstance(result, docker.tls.TLSConfig)
  181. assert result.ssl_version == ssl.PROTOCOL_TLSv1
  182. # verify is a special case - since `--tlsverify` = False means it
  183. # wasn't used, we set it if either the environment or the flag is True
  184. # see https://github.com/docker/compose/issues/5632
  185. assert result.verify is True
  186. def test_tls_verify_env_falsy_value(self):
  187. environment = Environment({'DOCKER_TLS_VERIFY': '0'})
  188. options = {'--tls': True}
  189. assert tls_config_from_options(options, environment) is True
  190. def test_tls_verify_default_cert_path(self):
  191. environment = Environment({'DOCKER_TLS_VERIFY': '1'})
  192. options = {'--tls': True}
  193. with mock.patch('compose.cli.docker_client.default_cert_path') as dcp:
  194. dcp.return_value = 'tests/fixtures/tls/'
  195. result = tls_config_from_options(options, environment)
  196. assert isinstance(result, docker.tls.TLSConfig)
  197. assert result.verify is True
  198. assert result.ca_cert == self.ca_cert
  199. assert result.cert == (self.client_cert, self.key)
  200. class TestGetTlsVersion(object):
  201. def test_get_tls_version_default(self):
  202. environment = {}
  203. assert get_tls_version(environment) is None
  204. @pytest.mark.skipif(not hasattr(ssl, 'PROTOCOL_TLSv1_2'), reason='TLS v1.2 unsupported')
  205. def test_get_tls_version_upgrade(self):
  206. environment = {'COMPOSE_TLS_VERSION': 'TLSv1_2'}
  207. assert get_tls_version(environment) == ssl.PROTOCOL_TLSv1_2
  208. def test_get_tls_version_unavailable(self):
  209. environment = {'COMPOSE_TLS_VERSION': 'TLSv5_5'}
  210. with mock.patch('compose.cli.docker_client.log') as mock_log:
  211. tls_version = get_tls_version(environment)
  212. mock_log.warn.assert_called_once_with(mock.ANY)
  213. assert tls_version is None