main_test.py 8.8 KB

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