service_test.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. from __future__ import unicode_literals
  2. from __future__ import absolute_import
  3. import os
  4. from .. import unittest
  5. import mock
  6. import docker
  7. from fig import Service
  8. from fig.container import Container
  9. from fig.service import (
  10. ConfigError,
  11. split_port,
  12. parse_volume_spec,
  13. build_volume_binding,
  14. )
  15. class ServiceTest(unittest.TestCase):
  16. def setUp(self):
  17. self.mock_client = mock.create_autospec(docker.Client)
  18. def test_name_validations(self):
  19. self.assertRaises(ConfigError, lambda: Service(name=''))
  20. self.assertRaises(ConfigError, lambda: Service(name=' '))
  21. self.assertRaises(ConfigError, lambda: Service(name='/'))
  22. self.assertRaises(ConfigError, lambda: Service(name='!'))
  23. self.assertRaises(ConfigError, lambda: Service(name='\xe2'))
  24. self.assertRaises(ConfigError, lambda: Service(name='_'))
  25. self.assertRaises(ConfigError, lambda: Service(name='____'))
  26. self.assertRaises(ConfigError, lambda: Service(name='foo_bar'))
  27. self.assertRaises(ConfigError, lambda: Service(name='__foo_bar__'))
  28. Service('a')
  29. Service('foo')
  30. def test_project_validation(self):
  31. self.assertRaises(ConfigError, lambda: Service(name='foo', project='_'))
  32. Service(name='foo', project='bar')
  33. def test_config_validation(self):
  34. self.assertRaises(ConfigError, lambda: Service(name='foo', port=['8000']))
  35. Service(name='foo', ports=['8000'])
  36. def test_get_volumes_from_container(self):
  37. container_id = 'aabbccddee'
  38. service = Service(
  39. 'test',
  40. volumes_from=[mock.Mock(id=container_id, spec=Container)])
  41. self.assertEqual(service._get_volumes_from(), [container_id])
  42. def test_get_volumes_from_intermediate_container(self):
  43. container_id = 'aabbccddee'
  44. service = Service('test')
  45. container = mock.Mock(id=container_id, spec=Container)
  46. self.assertEqual(service._get_volumes_from(container), [container_id])
  47. def test_get_volumes_from_service_container_exists(self):
  48. container_ids = ['aabbccddee', '12345']
  49. from_service = mock.create_autospec(Service)
  50. from_service.containers.return_value = [
  51. mock.Mock(id=container_id, spec=Container)
  52. for container_id in container_ids
  53. ]
  54. service = Service('test', volumes_from=[from_service])
  55. self.assertEqual(service._get_volumes_from(), container_ids)
  56. def test_get_volumes_from_service_no_container(self):
  57. container_id = 'abababab'
  58. from_service = mock.create_autospec(Service)
  59. from_service.containers.return_value = []
  60. from_service.create_container.return_value = mock.Mock(
  61. id=container_id,
  62. spec=Container)
  63. service = Service('test', volumes_from=[from_service])
  64. self.assertEqual(service._get_volumes_from(), [container_id])
  65. from_service.create_container.assert_called_once_with()
  66. def test_split_port_with_host_ip(self):
  67. internal_port, external_port = split_port("127.0.0.1:1000:2000")
  68. self.assertEqual(internal_port, "2000")
  69. self.assertEqual(external_port, ("127.0.0.1", "1000"))
  70. def test_split_port_with_protocol(self):
  71. internal_port, external_port = split_port("127.0.0.1:1000:2000/udp")
  72. self.assertEqual(internal_port, "2000/udp")
  73. self.assertEqual(external_port, ("127.0.0.1", "1000"))
  74. def test_split_port_with_host_ip_no_port(self):
  75. internal_port, external_port = split_port("127.0.0.1::2000")
  76. self.assertEqual(internal_port, "2000")
  77. self.assertEqual(external_port, ("127.0.0.1", None))
  78. def test_split_port_with_host_port(self):
  79. internal_port, external_port = split_port("1000:2000")
  80. self.assertEqual(internal_port, "2000")
  81. self.assertEqual(external_port, "1000")
  82. def test_split_port_no_host_port(self):
  83. internal_port, external_port = split_port("2000")
  84. self.assertEqual(internal_port, "2000")
  85. self.assertEqual(external_port, None)
  86. def test_split_port_invalid(self):
  87. with self.assertRaises(ConfigError):
  88. split_port("0.0.0.0:1000:2000:tcp")
  89. def test_split_domainname_none(self):
  90. service = Service('foo', hostname='name', client=self.mock_client)
  91. self.mock_client.containers.return_value = []
  92. opts = service._get_container_create_options({})
  93. self.assertEqual(opts['hostname'], 'name', 'hostname')
  94. self.assertFalse('domainname' in opts, 'domainname')
  95. def test_split_domainname_fqdn(self):
  96. service = Service('foo',
  97. hostname='name.domain.tld',
  98. client=self.mock_client)
  99. self.mock_client.containers.return_value = []
  100. opts = service._get_container_create_options({})
  101. self.assertEqual(opts['hostname'], 'name', 'hostname')
  102. self.assertEqual(opts['domainname'], 'domain.tld', 'domainname')
  103. def test_split_domainname_both(self):
  104. service = Service('foo',
  105. hostname='name',
  106. domainname='domain.tld',
  107. client=self.mock_client)
  108. self.mock_client.containers.return_value = []
  109. opts = service._get_container_create_options({})
  110. self.assertEqual(opts['hostname'], 'name', 'hostname')
  111. self.assertEqual(opts['domainname'], 'domain.tld', 'domainname')
  112. def test_split_domainname_weird(self):
  113. service = Service('foo',
  114. hostname='name.sub',
  115. domainname='domain.tld',
  116. client=self.mock_client)
  117. self.mock_client.containers.return_value = []
  118. opts = service._get_container_create_options({})
  119. self.assertEqual(opts['hostname'], 'name.sub', 'hostname')
  120. self.assertEqual(opts['domainname'], 'domain.tld', 'domainname')
  121. def test_get_container_not_found(self):
  122. mock_client = mock.create_autospec(docker.Client)
  123. mock_client.containers.return_value = []
  124. service = Service('foo', client=mock_client)
  125. self.assertRaises(ValueError, service.get_container)
  126. @mock.patch('fig.service.Container', autospec=True)
  127. def test_get_container(self, mock_container_class):
  128. mock_client = mock.create_autospec(docker.Client)
  129. container_dict = dict(Name='default_foo_2')
  130. mock_client.containers.return_value = [container_dict]
  131. service = Service('foo', client=mock_client)
  132. container = service.get_container(number=2)
  133. self.assertEqual(container, mock_container_class.from_ps.return_value)
  134. mock_container_class.from_ps.assert_called_once_with(
  135. mock_client, container_dict)
  136. @mock.patch('fig.service.log', autospec=True)
  137. def test_pull_image(self, mock_log):
  138. service = Service('foo', client=self.mock_client, image='someimage:sometag')
  139. service.pull(insecure_registry=True)
  140. self.mock_client.pull.assert_called_once_with('someimage:sometag', insecure_registry=True)
  141. mock_log.info.assert_called_once_with('Pulling foo (someimage:sometag)...')
  142. class ServiceVolumesTest(unittest.TestCase):
  143. def test_parse_volume_spec_only_one_path(self):
  144. spec = parse_volume_spec('/the/volume')
  145. self.assertEqual(spec, (None, '/the/volume', 'rw'))
  146. def test_parse_volume_spec_internal_and_external(self):
  147. spec = parse_volume_spec('external:interval')
  148. self.assertEqual(spec, ('external', 'interval', 'rw'))
  149. def test_parse_volume_spec_with_mode(self):
  150. spec = parse_volume_spec('external:interval:ro')
  151. self.assertEqual(spec, ('external', 'interval', 'ro'))
  152. def test_parse_volume_spec_too_many_parts(self):
  153. with self.assertRaises(ConfigError):
  154. parse_volume_spec('one:two:three:four')
  155. def test_parse_volume_bad_mode(self):
  156. with self.assertRaises(ConfigError):
  157. parse_volume_spec('one:two:notrw')
  158. def test_build_volume_binding(self):
  159. binding = build_volume_binding(parse_volume_spec('/outside:/inside'))
  160. self.assertEqual(
  161. binding,
  162. ('/outside', dict(bind='/inside', ro=False)))
  163. @mock.patch.dict(os.environ)
  164. def test_build_volume_binding_with_environ(self):
  165. os.environ['VOLUME_PATH'] = '/opt'
  166. binding = build_volume_binding(parse_volume_spec('${VOLUME_PATH}:/opt'))
  167. self.assertEqual(binding, ('/opt', dict(bind='/opt', ro=False)))
  168. @mock.patch.dict(os.environ)
  169. def test_building_volume_binding_with_home(self):
  170. os.environ['HOME'] = '/home/user'
  171. binding = build_volume_binding(parse_volume_spec('~:/home/user'))
  172. self.assertEqual(
  173. binding,
  174. ('/home/user', dict(bind='/home/user', ro=False)))