docker_client_test.py 8.9 KB

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