service_test.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. from __future__ import unicode_literals
  2. from __future__ import absolute_import
  3. import os
  4. from fig import Service
  5. from fig.service import CannotBeScaledError
  6. from fig.container import Container
  7. from fig.packages.docker.errors import APIError
  8. from .testcases import DockerClientTestCase
  9. class ServiceTest(DockerClientTestCase):
  10. def test_containers(self):
  11. foo = self.create_service('foo')
  12. bar = self.create_service('bar')
  13. foo.start_container()
  14. self.assertEqual(len(foo.containers()), 1)
  15. self.assertEqual(foo.containers()[0].name, 'figtest_foo_1')
  16. self.assertEqual(len(bar.containers()), 0)
  17. bar.start_container()
  18. bar.start_container()
  19. self.assertEqual(len(foo.containers()), 1)
  20. self.assertEqual(len(bar.containers()), 2)
  21. names = [c.name for c in bar.containers()]
  22. self.assertIn('figtest_bar_1', names)
  23. self.assertIn('figtest_bar_2', names)
  24. def test_containers_one_off(self):
  25. db = self.create_service('db')
  26. container = db.create_container(one_off=True)
  27. self.assertEqual(db.containers(stopped=True), [])
  28. self.assertEqual(db.containers(one_off=True, stopped=True), [container])
  29. def test_project_is_added_to_container_name(self):
  30. service = self.create_service('web')
  31. service.start_container()
  32. self.assertEqual(service.containers()[0].name, 'figtest_web_1')
  33. def test_start_stop(self):
  34. service = self.create_service('scalingtest')
  35. self.assertEqual(len(service.containers(stopped=True)), 0)
  36. service.create_container()
  37. self.assertEqual(len(service.containers()), 0)
  38. self.assertEqual(len(service.containers(stopped=True)), 1)
  39. service.start()
  40. self.assertEqual(len(service.containers()), 1)
  41. self.assertEqual(len(service.containers(stopped=True)), 1)
  42. service.stop(timeout=1)
  43. self.assertEqual(len(service.containers()), 0)
  44. self.assertEqual(len(service.containers(stopped=True)), 1)
  45. service.stop(timeout=1)
  46. self.assertEqual(len(service.containers()), 0)
  47. self.assertEqual(len(service.containers(stopped=True)), 1)
  48. def test_kill_remove(self):
  49. service = self.create_service('scalingtest')
  50. service.start_container()
  51. self.assertEqual(len(service.containers()), 1)
  52. service.remove_stopped()
  53. self.assertEqual(len(service.containers()), 1)
  54. service.kill()
  55. self.assertEqual(len(service.containers()), 0)
  56. self.assertEqual(len(service.containers(stopped=True)), 1)
  57. service.remove_stopped()
  58. self.assertEqual(len(service.containers(stopped=True)), 0)
  59. def test_create_container_with_one_off(self):
  60. db = self.create_service('db')
  61. container = db.create_container(one_off=True)
  62. self.assertEqual(container.name, 'figtest_db_run_1')
  63. def test_create_container_with_one_off_when_existing_container_is_running(self):
  64. db = self.create_service('db')
  65. db.start()
  66. container = db.create_container(one_off=True)
  67. self.assertEqual(container.name, 'figtest_db_run_1')
  68. def test_create_container_with_unspecified_volume(self):
  69. service = self.create_service('db', volumes=['/var/db'])
  70. container = service.create_container()
  71. service.start_container(container)
  72. self.assertIn('/var/db', container.inspect()['Volumes'])
  73. def test_create_container_with_specified_volume(self):
  74. service = self.create_service('db', volumes=['/tmp:/host-tmp'])
  75. container = service.create_container()
  76. service.start_container(container)
  77. self.assertIn('/host-tmp', container.inspect()['Volumes'])
  78. def test_create_container_with_volumes_from(self):
  79. volume_service = self.create_service('data')
  80. volume_container_1 = volume_service.create_container()
  81. volume_container_2 = Container.create(self.client, image='busybox:latest', command=["/bin/sleep", "300"])
  82. host_service = self.create_service('host', volumes_from=[volume_service, volume_container_2])
  83. host_container = host_service.create_container()
  84. host_service.start_container(host_container)
  85. self.assertIn(volume_container_1.id, host_container.inspect()['HostConfig']['VolumesFrom'])
  86. self.assertIn(volume_container_2.id, host_container.inspect()['HostConfig']['VolumesFrom'])
  87. def test_recreate_containers(self):
  88. service = self.create_service(
  89. 'db',
  90. environment={'FOO': '1'},
  91. volumes=['/var/db'],
  92. entrypoint=['sleep'],
  93. command=['300']
  94. )
  95. old_container = service.create_container()
  96. self.assertEqual(old_container.dictionary['Config']['Entrypoint'], ['sleep'])
  97. self.assertEqual(old_container.dictionary['Config']['Cmd'], ['300'])
  98. self.assertIn('FOO=1', old_container.dictionary['Config']['Env'])
  99. self.assertEqual(old_container.name, 'figtest_db_1')
  100. service.start_container(old_container)
  101. volume_path = old_container.inspect()['Volumes']['/var/db']
  102. num_containers_before = len(self.client.containers(all=True))
  103. service.options['environment']['FOO'] = '2'
  104. tuples = service.recreate_containers()
  105. self.assertEqual(len(tuples), 1)
  106. intermediate_container = tuples[0][0]
  107. new_container = tuples[0][1]
  108. self.assertEqual(intermediate_container.dictionary['Config']['Entrypoint'], ['echo'])
  109. self.assertEqual(new_container.dictionary['Config']['Entrypoint'], ['sleep'])
  110. self.assertEqual(new_container.dictionary['Config']['Cmd'], ['300'])
  111. self.assertIn('FOO=2', new_container.dictionary['Config']['Env'])
  112. self.assertEqual(new_container.name, 'figtest_db_1')
  113. self.assertEqual(new_container.inspect()['Volumes']['/var/db'], volume_path)
  114. self.assertIn(intermediate_container.id, new_container.dictionary['HostConfig']['VolumesFrom'])
  115. self.assertEqual(len(self.client.containers(all=True)), num_containers_before)
  116. self.assertNotEqual(old_container.id, new_container.id)
  117. self.assertRaises(APIError,
  118. self.client.inspect_container,
  119. intermediate_container.id)
  120. def test_recreate_containers_when_containers_are_stopped(self):
  121. service = self.create_service(
  122. 'db',
  123. environment={'FOO': '1'},
  124. volumes=['/var/db'],
  125. entrypoint=['sleep'],
  126. command=['300']
  127. )
  128. old_container = service.create_container()
  129. self.assertEqual(len(service.containers(stopped=True)), 1)
  130. service.recreate_containers()
  131. self.assertEqual(len(service.containers(stopped=True)), 1)
  132. def test_start_container_passes_through_options(self):
  133. db = self.create_service('db')
  134. db.start_container(environment={'FOO': 'BAR'})
  135. self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')
  136. def test_start_container_inherits_options_from_constructor(self):
  137. db = self.create_service('db', environment={'FOO': 'BAR'})
  138. db.start_container()
  139. self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')
  140. def test_start_container_creates_links(self):
  141. db = self.create_service('db')
  142. web = self.create_service('web', links=[(db, None)])
  143. db.start_container()
  144. web.start_container()
  145. self.assertIn('figtest_db_1', web.containers()[0].links())
  146. self.assertIn('db_1', web.containers()[0].links())
  147. def test_start_container_creates_links_with_names(self):
  148. db = self.create_service('db')
  149. web = self.create_service('web', links=[(db, 'custom_link_name')])
  150. db.start_container()
  151. web.start_container()
  152. self.assertIn('custom_link_name', web.containers()[0].links())
  153. def test_start_normal_container_does_not_create_links_to_its_own_service(self):
  154. db = self.create_service('db')
  155. c1 = db.start_container()
  156. c2 = db.start_container()
  157. self.assertNotIn(c1.name, c2.links())
  158. def test_start_one_off_container_creates_links_to_its_own_service(self):
  159. db = self.create_service('db')
  160. c1 = db.start_container()
  161. c2 = db.start_container(one_off=True)
  162. self.assertIn(c1.name, c2.links())
  163. def test_start_container_builds_images(self):
  164. service = Service(
  165. name='test',
  166. client=self.client,
  167. build='tests/fixtures/simple-dockerfile',
  168. project='figtest',
  169. )
  170. container = service.start_container()
  171. container.wait()
  172. self.assertIn('success', container.logs())
  173. self.assertEqual(len(self.client.images(name='figtest_test')), 1)
  174. def test_start_container_uses_tagged_image_if_it_exists(self):
  175. self.client.build('tests/fixtures/simple-dockerfile', tag='figtest_test')
  176. service = Service(
  177. name='test',
  178. client=self.client,
  179. build='this/does/not/exist/and/will/throw/error',
  180. project='figtest',
  181. )
  182. container = service.start_container()
  183. container.wait()
  184. self.assertIn('success', container.logs())
  185. def test_start_container_creates_ports(self):
  186. service = self.create_service('web', ports=[8000])
  187. container = service.start_container().inspect()
  188. self.assertEqual(list(container['NetworkSettings']['Ports'].keys()), ['8000/tcp'])
  189. self.assertNotEqual(container['NetworkSettings']['Ports']['8000/tcp'][0]['HostPort'], '8000')
  190. def test_start_container_stays_unpriviliged(self):
  191. service = self.create_service('web')
  192. container = service.start_container().inspect()
  193. self.assertEqual(container['HostConfig']['Privileged'], False)
  194. def test_start_container_becomes_priviliged(self):
  195. service = self.create_service('web', privileged = True)
  196. container = service.start_container().inspect()
  197. self.assertEqual(container['HostConfig']['Privileged'], True)
  198. def test_expose_does_not_publish_ports(self):
  199. service = self.create_service('web', expose=[8000])
  200. container = service.start_container().inspect()
  201. self.assertEqual(container['NetworkSettings']['Ports'], {'8000/tcp': None})
  202. def test_start_container_creates_port_with_explicit_protocol(self):
  203. service = self.create_service('web', ports=['8000/udp'])
  204. container = service.start_container().inspect()
  205. self.assertEqual(list(container['NetworkSettings']['Ports'].keys()), ['8000/udp'])
  206. def test_start_container_creates_fixed_external_ports(self):
  207. service = self.create_service('web', ports=['8000:8000'])
  208. container = service.start_container().inspect()
  209. self.assertIn('8000/tcp', container['NetworkSettings']['Ports'])
  210. self.assertEqual(container['NetworkSettings']['Ports']['8000/tcp'][0]['HostPort'], '8000')
  211. def test_start_container_creates_fixed_external_ports_when_it_is_different_to_internal_port(self):
  212. service = self.create_service('web', ports=['8001:8000'])
  213. container = service.start_container().inspect()
  214. self.assertIn('8000/tcp', container['NetworkSettings']['Ports'])
  215. self.assertEqual(container['NetworkSettings']['Ports']['8000/tcp'][0]['HostPort'], '8001')
  216. def test_port_with_explicit_interface(self):
  217. service = self.create_service('web', ports=[
  218. '127.0.0.1:8001:8000',
  219. '0.0.0.0:9001:9000/udp',
  220. ])
  221. container = service.start_container().inspect()
  222. self.assertEqual(container['NetworkSettings']['Ports'], {
  223. '8000/tcp': [
  224. {
  225. 'HostIp': '127.0.0.1',
  226. 'HostPort': '8001',
  227. },
  228. ],
  229. '9000/udp': [
  230. {
  231. 'HostIp': '0.0.0.0',
  232. 'HostPort': '9001',
  233. },
  234. ],
  235. })
  236. def test_scale(self):
  237. service = self.create_service('web')
  238. service.scale(1)
  239. self.assertEqual(len(service.containers()), 1)
  240. service.scale(3)
  241. self.assertEqual(len(service.containers()), 3)
  242. service.scale(1)
  243. self.assertEqual(len(service.containers()), 1)
  244. service.scale(0)
  245. self.assertEqual(len(service.containers()), 0)
  246. def test_scale_on_service_that_cannot_be_scaled(self):
  247. service = self.create_service('web', ports=['8000:8000'])
  248. self.assertRaises(CannotBeScaledError, lambda: service.scale(1))
  249. def test_scale_sets_ports(self):
  250. service = self.create_service('web', ports=['8000'])
  251. service.scale(2)
  252. containers = service.containers()
  253. self.assertEqual(len(containers), 2)
  254. for container in containers:
  255. self.assertEqual(list(container.inspect()['HostConfig']['PortBindings'].keys()), ['8000/tcp'])
  256. def test_network_mode_none(self):
  257. service = self.create_service('web', net='none')
  258. container = service.start_container().inspect()
  259. self.assertEqual(container['HostConfig']['NetworkMode'], 'none')
  260. def test_network_mode_bridged(self):
  261. service = self.create_service('web', net='bridge')
  262. container = service.start_container().inspect()
  263. self.assertEqual(container['HostConfig']['NetworkMode'], 'bridge')
  264. def test_network_mode_host(self):
  265. service = self.create_service('web', net='host')
  266. container = service.start_container().inspect()
  267. self.assertEqual(container['HostConfig']['NetworkMode'], 'host')
  268. def test_dns_single_value(self):
  269. service = self.create_service('web', dns='8.8.8.8')
  270. container = service.start_container().inspect()
  271. self.assertEqual(container['HostConfig']['Dns'], ['8.8.8.8'])
  272. def test_dns_list(self):
  273. service = self.create_service('web', dns=['8.8.8.8', '9.9.9.9'])
  274. container = service.start_container().inspect()
  275. self.assertEqual(container['HostConfig']['Dns'], ['8.8.8.8', '9.9.9.9'])
  276. def test_working_dir_param(self):
  277. service = self.create_service('container', working_dir='/working/dir/sample')
  278. container = service.create_container().inspect()
  279. self.assertEqual(container['Config']['WorkingDir'], '/working/dir/sample')
  280. def test_split_env(self):
  281. service = self.create_service('web', environment=['NORMAL=F1', 'CONTAINS_EQUALS=F=2', 'TRAILING_EQUALS='])
  282. env = service.start_container().environment
  283. for k,v in {'NORMAL': 'F1', 'CONTAINS_EQUALS': 'F=2', 'TRAILING_EQUALS': ''}.iteritems():
  284. self.assertEqual(env[k], v)
  285. def test_resolve_env(self):
  286. service = self.create_service('web', environment={'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': None, 'NO_DEF': None})
  287. os.environ['FILE_DEF'] = 'E1'
  288. os.environ['FILE_DEF_EMPTY'] = 'E2'
  289. os.environ['ENV_DEF'] = 'E3'
  290. try:
  291. env = service.start_container().environment
  292. for k,v in {'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''}.iteritems():
  293. self.assertEqual(env[k], v)
  294. finally:
  295. del os.environ['FILE_DEF']
  296. del os.environ['FILE_DEF_EMPTY']
  297. del os.environ['ENV_DEF']