main_test.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import logging
  4. import docker
  5. import pytest
  6. from compose import container
  7. from compose.cli.errors import UserError
  8. from compose.cli.formatter import ConsoleWarningFormatter
  9. from compose.cli.main import build_one_off_container_options
  10. from compose.cli.main import call_docker
  11. from compose.cli.main import convergence_strategy_from_opts
  12. from compose.cli.main import filter_containers_to_service_names
  13. from compose.cli.main import get_docker_start_call
  14. from compose.cli.main import setup_console_handler
  15. from compose.cli.main import warn_for_swarm_mode
  16. from compose.service import ConvergenceStrategy
  17. from tests import mock
  18. def mock_container(service, number):
  19. return mock.create_autospec(
  20. container.Container,
  21. service=service,
  22. number=number,
  23. name_without_project='{0}_{1}'.format(service, number))
  24. @pytest.fixture
  25. def logging_handler():
  26. stream = mock.Mock()
  27. stream.isatty.return_value = True
  28. return logging.StreamHandler(stream=stream)
  29. class TestCLIMainTestCase(object):
  30. def test_filter_containers_to_service_names(self):
  31. containers = [
  32. mock_container('web', 1),
  33. mock_container('web', 2),
  34. mock_container('db', 1),
  35. mock_container('other', 1),
  36. mock_container('another', 1),
  37. ]
  38. service_names = ['web', 'db']
  39. actual = filter_containers_to_service_names(containers, service_names)
  40. assert actual == containers[:3]
  41. def test_filter_containers_to_service_names_all(self):
  42. containers = [
  43. mock_container('web', 1),
  44. mock_container('db', 1),
  45. mock_container('other', 1),
  46. ]
  47. service_names = []
  48. actual = filter_containers_to_service_names(containers, service_names)
  49. assert actual == containers
  50. def test_warning_in_swarm_mode(self):
  51. mock_client = mock.create_autospec(docker.APIClient)
  52. mock_client.info.return_value = {'Swarm': {'LocalNodeState': 'active'}}
  53. with mock.patch('compose.cli.main.log') as fake_log:
  54. warn_for_swarm_mode(mock_client)
  55. assert fake_log.warning.call_count == 1
  56. def test_build_one_off_container_options(self):
  57. command = 'build myservice'
  58. detach = False
  59. options = {
  60. '-e': ['MYVAR=MYVALUE'],
  61. '-T': True,
  62. '--label': ['MYLABEL'],
  63. '--entrypoint': 'bash',
  64. '--user': 'MYUSER',
  65. '--service-ports': [],
  66. '--publish': '',
  67. '--name': 'MYNAME',
  68. '--workdir': '.',
  69. '--volume': [],
  70. 'stdin_open': False,
  71. }
  72. expected_container_options = {
  73. 'command': command,
  74. 'tty': False,
  75. 'stdin_open': False,
  76. 'detach': detach,
  77. 'entrypoint': 'bash',
  78. 'environment': {'MYVAR': 'MYVALUE'},
  79. 'labels': {'MYLABEL': ''},
  80. 'name': 'MYNAME',
  81. 'ports': [],
  82. 'restart': None,
  83. 'user': 'MYUSER',
  84. 'working_dir': '.',
  85. }
  86. container_options = build_one_off_container_options(options, detach, command)
  87. assert container_options == expected_container_options
  88. def test_get_docker_start_call(self):
  89. container_id = 'my_container_id'
  90. mock_container_options = {'detach': False, 'stdin_open': True}
  91. expected_docker_start_call = ['start', '--attach', '--interactive', container_id]
  92. docker_start_call = get_docker_start_call(mock_container_options, container_id)
  93. assert expected_docker_start_call == docker_start_call
  94. mock_container_options = {'detach': False, 'stdin_open': False}
  95. expected_docker_start_call = ['start', '--attach', container_id]
  96. docker_start_call = get_docker_start_call(mock_container_options, container_id)
  97. assert expected_docker_start_call == docker_start_call
  98. mock_container_options = {'detach': True, 'stdin_open': True}
  99. expected_docker_start_call = ['start', '--interactive', container_id]
  100. docker_start_call = get_docker_start_call(mock_container_options, container_id)
  101. assert expected_docker_start_call == docker_start_call
  102. mock_container_options = {'detach': True, 'stdin_open': False}
  103. expected_docker_start_call = ['start', container_id]
  104. docker_start_call = get_docker_start_call(mock_container_options, container_id)
  105. assert expected_docker_start_call == docker_start_call
  106. class TestSetupConsoleHandlerTestCase(object):
  107. def test_with_tty_verbose(self, logging_handler):
  108. setup_console_handler(logging_handler, True)
  109. assert type(logging_handler.formatter) == ConsoleWarningFormatter
  110. assert '%(name)s' in logging_handler.formatter._fmt
  111. assert '%(funcName)s' in logging_handler.formatter._fmt
  112. def test_with_tty_not_verbose(self, logging_handler):
  113. setup_console_handler(logging_handler, False)
  114. assert type(logging_handler.formatter) == ConsoleWarningFormatter
  115. assert '%(name)s' not in logging_handler.formatter._fmt
  116. assert '%(funcName)s' not in logging_handler.formatter._fmt
  117. def test_with_not_a_tty(self, logging_handler):
  118. logging_handler.stream.isatty.return_value = False
  119. setup_console_handler(logging_handler, False)
  120. assert type(logging_handler.formatter) == logging.Formatter
  121. class TestConvergeStrategyFromOptsTestCase(object):
  122. def test_invalid_opts(self):
  123. options = {'--force-recreate': True, '--no-recreate': True}
  124. with pytest.raises(UserError):
  125. convergence_strategy_from_opts(options)
  126. def test_always(self):
  127. options = {'--force-recreate': True, '--no-recreate': False}
  128. assert (
  129. convergence_strategy_from_opts(options) ==
  130. ConvergenceStrategy.always
  131. )
  132. def test_never(self):
  133. options = {'--force-recreate': False, '--no-recreate': True}
  134. assert (
  135. convergence_strategy_from_opts(options) ==
  136. ConvergenceStrategy.never
  137. )
  138. def test_changed(self):
  139. options = {'--force-recreate': False, '--no-recreate': False}
  140. assert (
  141. convergence_strategy_from_opts(options) ==
  142. ConvergenceStrategy.changed
  143. )
  144. def mock_find_executable(exe):
  145. return exe
  146. @mock.patch('compose.cli.main.find_executable', mock_find_executable)
  147. class TestCallDocker(object):
  148. def test_simple_no_options(self):
  149. with mock.patch('subprocess.call') as fake_call:
  150. call_docker(['ps'], {}, {})
  151. assert fake_call.call_args[0][0] == ['docker', 'ps']
  152. def test_simple_tls_option(self):
  153. with mock.patch('subprocess.call') as fake_call:
  154. call_docker(['ps'], {'--tls': True}, {})
  155. assert fake_call.call_args[0][0] == ['docker', '--tls', 'ps']
  156. def test_advanced_tls_options(self):
  157. with mock.patch('subprocess.call') as fake_call:
  158. call_docker(['ps'], {
  159. '--tls': True,
  160. '--tlscacert': './ca.pem',
  161. '--tlscert': './cert.pem',
  162. '--tlskey': './key.pem',
  163. }, {})
  164. assert fake_call.call_args[0][0] == [
  165. 'docker', '--tls', '--tlscacert', './ca.pem', '--tlscert',
  166. './cert.pem', '--tlskey', './key.pem', 'ps'
  167. ]
  168. def test_with_host_option(self):
  169. with mock.patch('subprocess.call') as fake_call:
  170. call_docker(['ps'], {'--host': 'tcp://mydocker.net:2333'}, {})
  171. assert fake_call.call_args[0][0] == [
  172. 'docker', '--host', 'tcp://mydocker.net:2333', 'ps'
  173. ]
  174. def test_with_http_host(self):
  175. with mock.patch('subprocess.call') as fake_call:
  176. call_docker(['ps'], {'--host': 'http://mydocker.net:2333'}, {})
  177. assert fake_call.call_args[0][0] == [
  178. 'docker', '--host', 'tcp://mydocker.net:2333', 'ps',
  179. ]
  180. def test_with_host_option_shorthand_equal(self):
  181. with mock.patch('subprocess.call') as fake_call:
  182. call_docker(['ps'], {'--host': '=tcp://mydocker.net:2333'}, {})
  183. assert fake_call.call_args[0][0] == [
  184. 'docker', '--host', 'tcp://mydocker.net:2333', 'ps'
  185. ]
  186. def test_with_env(self):
  187. with mock.patch('subprocess.call') as fake_call:
  188. call_docker(['ps'], {}, {'DOCKER_HOST': 'tcp://mydocker.net:2333'})
  189. assert fake_call.call_args[0][0] == [
  190. 'docker', 'ps'
  191. ]
  192. assert fake_call.call_args[1]['env'] == {'DOCKER_HOST': 'tcp://mydocker.net:2333'}