1
0

docker_client_test.py 9.5 KB

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