1
0

project_test.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import random
  4. import py
  5. import pytest
  6. from docker.errors import NotFound
  7. from .testcases import DockerClientTestCase
  8. from compose.config import config
  9. from compose.config import ConfigurationError
  10. from compose.config.types import VolumeFromSpec
  11. from compose.config.types import VolumeSpec
  12. from compose.const import LABEL_PROJECT
  13. from compose.container import Container
  14. from compose.project import Project
  15. from compose.service import ConvergenceStrategy
  16. from tests.integration.testcases import v2_only
  17. def build_service_dicts(service_config):
  18. return config.load(
  19. config.ConfigDetails(
  20. 'working_dir',
  21. [config.ConfigFile(None, service_config)]))
  22. class ProjectTest(DockerClientTestCase):
  23. def test_containers(self):
  24. web = self.create_service('web')
  25. db = self.create_service('db')
  26. project = Project('composetest', [web, db], self.client)
  27. project.up()
  28. containers = project.containers()
  29. self.assertEqual(len(containers), 2)
  30. def test_containers_with_service_names(self):
  31. web = self.create_service('web')
  32. db = self.create_service('db')
  33. project = Project('composetest', [web, db], self.client)
  34. project.up()
  35. containers = project.containers(['web'])
  36. self.assertEqual(
  37. [c.name for c in containers],
  38. ['composetest_web_1'])
  39. def test_containers_with_extra_service(self):
  40. web = self.create_service('web')
  41. web_1 = web.create_container()
  42. db = self.create_service('db')
  43. db_1 = db.create_container()
  44. self.create_service('extra').create_container()
  45. project = Project('composetest', [web, db], self.client)
  46. self.assertEqual(
  47. set(project.containers(stopped=True)),
  48. set([web_1, db_1]),
  49. )
  50. def test_volumes_from_service(self):
  51. service_dicts = build_service_dicts({
  52. 'data': {
  53. 'image': 'busybox:latest',
  54. 'volumes': ['/var/data'],
  55. },
  56. 'db': {
  57. 'image': 'busybox:latest',
  58. 'volumes_from': ['data'],
  59. },
  60. })
  61. project = Project.from_config(
  62. name='composetest',
  63. config_data=service_dicts,
  64. client=self.client,
  65. )
  66. db = project.get_service('db')
  67. data = project.get_service('data')
  68. self.assertEqual(db.volumes_from, [VolumeFromSpec(data, 'rw', 'service')])
  69. def test_volumes_from_container(self):
  70. data_container = Container.create(
  71. self.client,
  72. image='busybox:latest',
  73. volumes=['/var/data'],
  74. name='composetest_data_container',
  75. labels={LABEL_PROJECT: 'composetest'},
  76. )
  77. project = Project.from_config(
  78. name='composetest',
  79. config_data=build_service_dicts({
  80. 'db': {
  81. 'image': 'busybox:latest',
  82. 'volumes_from': ['composetest_data_container'],
  83. },
  84. }),
  85. client=self.client,
  86. )
  87. db = project.get_service('db')
  88. self.assertEqual(db._get_volumes_from(), [data_container.id + ':rw'])
  89. @v2_only()
  90. def test_network_mode_from_service(self):
  91. project = Project.from_config(
  92. name='composetest',
  93. client=self.client,
  94. config_data=build_service_dicts({
  95. 'version': 2,
  96. 'services': {
  97. 'net': {
  98. 'image': 'busybox:latest',
  99. 'command': ["top"]
  100. },
  101. 'web': {
  102. 'image': 'busybox:latest',
  103. 'network_mode': 'service:net',
  104. 'command': ["top"]
  105. },
  106. },
  107. }),
  108. )
  109. project.up()
  110. web = project.get_service('web')
  111. net = project.get_service('net')
  112. self.assertEqual(web.net.mode, 'container:' + net.containers()[0].id)
  113. @v2_only()
  114. def test_network_mode_from_container(self):
  115. def get_project():
  116. return Project.from_config(
  117. name='composetest',
  118. config_data=build_service_dicts({
  119. 'version': 2,
  120. 'services': {
  121. 'web': {
  122. 'image': 'busybox:latest',
  123. 'network_mode': 'container:composetest_net_container'
  124. },
  125. },
  126. }),
  127. client=self.client,
  128. )
  129. with pytest.raises(ConfigurationError) as excinfo:
  130. get_project()
  131. assert "container 'composetest_net_container' which does not exist" in excinfo.exconly()
  132. net_container = Container.create(
  133. self.client,
  134. image='busybox:latest',
  135. name='composetest_net_container',
  136. command='top',
  137. labels={LABEL_PROJECT: 'composetest'},
  138. )
  139. net_container.start()
  140. project = get_project()
  141. project.up()
  142. web = project.get_service('web')
  143. self.assertEqual(web.net.mode, 'container:' + net_container.id)
  144. def test_net_from_service_v1(self):
  145. project = Project.from_config(
  146. name='composetest',
  147. config_data=build_service_dicts({
  148. 'net': {
  149. 'image': 'busybox:latest',
  150. 'command': ["top"]
  151. },
  152. 'web': {
  153. 'image': 'busybox:latest',
  154. 'net': 'container:net',
  155. 'command': ["top"]
  156. },
  157. }),
  158. client=self.client,
  159. )
  160. project.up()
  161. web = project.get_service('web')
  162. net = project.get_service('net')
  163. self.assertEqual(web.net.mode, 'container:' + net.containers()[0].id)
  164. def test_net_from_container_v1(self):
  165. def get_project():
  166. return Project.from_config(
  167. name='composetest',
  168. config_data=build_service_dicts({
  169. 'web': {
  170. 'image': 'busybox:latest',
  171. 'net': 'container:composetest_net_container'
  172. },
  173. }),
  174. client=self.client,
  175. )
  176. with pytest.raises(ConfigurationError) as excinfo:
  177. get_project()
  178. assert "container 'composetest_net_container' which does not exist" in excinfo.exconly()
  179. net_container = Container.create(
  180. self.client,
  181. image='busybox:latest',
  182. name='composetest_net_container',
  183. command='top',
  184. labels={LABEL_PROJECT: 'composetest'},
  185. )
  186. net_container.start()
  187. project = get_project()
  188. project.up()
  189. web = project.get_service('web')
  190. self.assertEqual(web.net.mode, 'container:' + net_container.id)
  191. def test_start_pause_unpause_stop_kill_remove(self):
  192. web = self.create_service('web')
  193. db = self.create_service('db')
  194. project = Project('composetest', [web, db], self.client)
  195. project.start()
  196. self.assertEqual(len(web.containers()), 0)
  197. self.assertEqual(len(db.containers()), 0)
  198. web_container_1 = web.create_container()
  199. web_container_2 = web.create_container()
  200. db_container = db.create_container()
  201. project.start(service_names=['web'])
  202. self.assertEqual(set(c.name for c in project.containers()), set([web_container_1.name, web_container_2.name]))
  203. project.start()
  204. self.assertEqual(set(c.name for c in project.containers()),
  205. set([web_container_1.name, web_container_2.name, db_container.name]))
  206. project.pause(service_names=['web'])
  207. self.assertEqual(set([c.name for c in project.containers() if c.is_paused]),
  208. set([web_container_1.name, web_container_2.name]))
  209. project.pause()
  210. self.assertEqual(set([c.name for c in project.containers() if c.is_paused]),
  211. set([web_container_1.name, web_container_2.name, db_container.name]))
  212. project.unpause(service_names=['db'])
  213. self.assertEqual(len([c.name for c in project.containers() if c.is_paused]), 2)
  214. project.unpause()
  215. self.assertEqual(len([c.name for c in project.containers() if c.is_paused]), 0)
  216. project.stop(service_names=['web'], timeout=1)
  217. self.assertEqual(set(c.name for c in project.containers()), set([db_container.name]))
  218. project.kill(service_names=['db'])
  219. self.assertEqual(len(project.containers()), 0)
  220. self.assertEqual(len(project.containers(stopped=True)), 3)
  221. project.remove_stopped(service_names=['web'])
  222. self.assertEqual(len(project.containers(stopped=True)), 1)
  223. project.remove_stopped()
  224. self.assertEqual(len(project.containers(stopped=True)), 0)
  225. def test_create(self):
  226. web = self.create_service('web')
  227. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  228. project = Project('composetest', [web, db], self.client)
  229. project.create(['db'])
  230. self.assertEqual(len(project.containers()), 0)
  231. self.assertEqual(len(project.containers(stopped=True)), 1)
  232. self.assertEqual(len(db.containers()), 0)
  233. self.assertEqual(len(db.containers(stopped=True)), 1)
  234. self.assertEqual(len(web.containers(stopped=True)), 0)
  235. def test_create_twice(self):
  236. web = self.create_service('web')
  237. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  238. project = Project('composetest', [web, db], self.client)
  239. project.create(['db', 'web'])
  240. project.create(['db', 'web'])
  241. self.assertEqual(len(project.containers()), 0)
  242. self.assertEqual(len(project.containers(stopped=True)), 2)
  243. self.assertEqual(len(db.containers()), 0)
  244. self.assertEqual(len(db.containers(stopped=True)), 1)
  245. self.assertEqual(len(web.containers()), 0)
  246. self.assertEqual(len(web.containers(stopped=True)), 1)
  247. def test_create_with_links(self):
  248. db = self.create_service('db')
  249. web = self.create_service('web', links=[(db, 'db')])
  250. project = Project('composetest', [db, web], self.client)
  251. project.create(['web'])
  252. self.assertEqual(len(project.containers()), 0)
  253. self.assertEqual(len(project.containers(stopped=True)), 2)
  254. self.assertEqual(len(db.containers()), 0)
  255. self.assertEqual(len(db.containers(stopped=True)), 1)
  256. self.assertEqual(len(web.containers()), 0)
  257. self.assertEqual(len(web.containers(stopped=True)), 1)
  258. def test_create_strategy_always(self):
  259. db = self.create_service('db')
  260. project = Project('composetest', [db], self.client)
  261. project.create(['db'])
  262. old_id = project.containers(stopped=True)[0].id
  263. project.create(['db'], strategy=ConvergenceStrategy.always)
  264. self.assertEqual(len(project.containers()), 0)
  265. self.assertEqual(len(project.containers(stopped=True)), 1)
  266. db_container = project.containers(stopped=True)[0]
  267. self.assertNotEqual(db_container.id, old_id)
  268. def test_create_strategy_never(self):
  269. db = self.create_service('db')
  270. project = Project('composetest', [db], self.client)
  271. project.create(['db'])
  272. old_id = project.containers(stopped=True)[0].id
  273. project.create(['db'], strategy=ConvergenceStrategy.never)
  274. self.assertEqual(len(project.containers()), 0)
  275. self.assertEqual(len(project.containers(stopped=True)), 1)
  276. db_container = project.containers(stopped=True)[0]
  277. self.assertEqual(db_container.id, old_id)
  278. def test_project_up(self):
  279. web = self.create_service('web')
  280. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  281. project = Project('composetest', [web, db], self.client)
  282. project.start()
  283. self.assertEqual(len(project.containers()), 0)
  284. project.up(['db'])
  285. self.assertEqual(len(project.containers()), 1)
  286. self.assertEqual(len(db.containers()), 1)
  287. self.assertEqual(len(web.containers()), 0)
  288. def test_project_up_starts_uncreated_services(self):
  289. db = self.create_service('db')
  290. web = self.create_service('web', links=[(db, 'db')])
  291. project = Project('composetest', [db, web], self.client)
  292. project.up(['db'])
  293. self.assertEqual(len(project.containers()), 1)
  294. project.up()
  295. self.assertEqual(len(project.containers()), 2)
  296. self.assertEqual(len(db.containers()), 1)
  297. self.assertEqual(len(web.containers()), 1)
  298. def test_recreate_preserves_volumes(self):
  299. web = self.create_service('web')
  300. db = self.create_service('db', volumes=[VolumeSpec.parse('/etc')])
  301. project = Project('composetest', [web, db], self.client)
  302. project.start()
  303. self.assertEqual(len(project.containers()), 0)
  304. project.up(['db'])
  305. self.assertEqual(len(project.containers()), 1)
  306. old_db_id = project.containers()[0].id
  307. db_volume_path = project.containers()[0].get('Volumes./etc')
  308. project.up(strategy=ConvergenceStrategy.always)
  309. self.assertEqual(len(project.containers()), 2)
  310. db_container = [c for c in project.containers() if 'db' in c.name][0]
  311. self.assertNotEqual(db_container.id, old_db_id)
  312. self.assertEqual(db_container.get('Volumes./etc'), db_volume_path)
  313. def test_project_up_with_no_recreate_running(self):
  314. web = self.create_service('web')
  315. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  316. project = Project('composetest', [web, db], self.client)
  317. project.start()
  318. self.assertEqual(len(project.containers()), 0)
  319. project.up(['db'])
  320. self.assertEqual(len(project.containers()), 1)
  321. old_db_id = project.containers()[0].id
  322. container, = project.containers()
  323. db_volume_path = container.get_mount('/var/db')['Source']
  324. project.up(strategy=ConvergenceStrategy.never)
  325. self.assertEqual(len(project.containers()), 2)
  326. db_container = [c for c in project.containers() if 'db' in c.name][0]
  327. self.assertEqual(db_container.id, old_db_id)
  328. self.assertEqual(
  329. db_container.get_mount('/var/db')['Source'],
  330. db_volume_path)
  331. def test_project_up_with_no_recreate_stopped(self):
  332. web = self.create_service('web')
  333. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  334. project = Project('composetest', [web, db], self.client)
  335. project.start()
  336. self.assertEqual(len(project.containers()), 0)
  337. project.up(['db'])
  338. project.kill()
  339. old_containers = project.containers(stopped=True)
  340. self.assertEqual(len(old_containers), 1)
  341. old_container, = old_containers
  342. old_db_id = old_container.id
  343. db_volume_path = old_container.get_mount('/var/db')['Source']
  344. project.up(strategy=ConvergenceStrategy.never)
  345. new_containers = project.containers(stopped=True)
  346. self.assertEqual(len(new_containers), 2)
  347. self.assertEqual([c.is_running for c in new_containers], [True, True])
  348. db_container = [c for c in new_containers if 'db' in c.name][0]
  349. self.assertEqual(db_container.id, old_db_id)
  350. self.assertEqual(
  351. db_container.get_mount('/var/db')['Source'],
  352. db_volume_path)
  353. def test_project_up_without_all_services(self):
  354. console = self.create_service('console')
  355. db = self.create_service('db')
  356. project = Project('composetest', [console, db], self.client)
  357. project.start()
  358. self.assertEqual(len(project.containers()), 0)
  359. project.up()
  360. self.assertEqual(len(project.containers()), 2)
  361. self.assertEqual(len(db.containers()), 1)
  362. self.assertEqual(len(console.containers()), 1)
  363. def test_project_up_starts_links(self):
  364. console = self.create_service('console')
  365. db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
  366. web = self.create_service('web', links=[(db, 'db')])
  367. project = Project('composetest', [web, db, console], self.client)
  368. project.start()
  369. self.assertEqual(len(project.containers()), 0)
  370. project.up(['web'])
  371. self.assertEqual(len(project.containers()), 2)
  372. self.assertEqual(len(web.containers()), 1)
  373. self.assertEqual(len(db.containers()), 1)
  374. self.assertEqual(len(console.containers()), 0)
  375. def test_project_up_starts_depends(self):
  376. project = Project.from_config(
  377. name='composetest',
  378. config_data=build_service_dicts({
  379. 'console': {
  380. 'image': 'busybox:latest',
  381. 'command': ["top"],
  382. },
  383. 'data': {
  384. 'image': 'busybox:latest',
  385. 'command': ["top"]
  386. },
  387. 'db': {
  388. 'image': 'busybox:latest',
  389. 'command': ["top"],
  390. 'volumes_from': ['data'],
  391. },
  392. 'web': {
  393. 'image': 'busybox:latest',
  394. 'command': ["top"],
  395. 'links': ['db'],
  396. },
  397. }),
  398. client=self.client,
  399. )
  400. project.start()
  401. self.assertEqual(len(project.containers()), 0)
  402. project.up(['web'])
  403. self.assertEqual(len(project.containers()), 3)
  404. self.assertEqual(len(project.get_service('web').containers()), 1)
  405. self.assertEqual(len(project.get_service('db').containers()), 1)
  406. self.assertEqual(len(project.get_service('data').containers()), 1)
  407. self.assertEqual(len(project.get_service('console').containers()), 0)
  408. def test_project_up_with_no_deps(self):
  409. project = Project.from_config(
  410. name='composetest',
  411. config_data=build_service_dicts({
  412. 'console': {
  413. 'image': 'busybox:latest',
  414. 'command': ["top"],
  415. },
  416. 'data': {
  417. 'image': 'busybox:latest',
  418. 'command': ["top"]
  419. },
  420. 'db': {
  421. 'image': 'busybox:latest',
  422. 'command': ["top"],
  423. 'volumes_from': ['data'],
  424. },
  425. 'web': {
  426. 'image': 'busybox:latest',
  427. 'command': ["top"],
  428. 'links': ['db'],
  429. },
  430. }),
  431. client=self.client,
  432. )
  433. project.start()
  434. self.assertEqual(len(project.containers()), 0)
  435. project.up(['db'], start_deps=False)
  436. self.assertEqual(len(project.containers(stopped=True)), 2)
  437. self.assertEqual(len(project.get_service('web').containers()), 0)
  438. self.assertEqual(len(project.get_service('db').containers()), 1)
  439. self.assertEqual(len(project.get_service('data').containers()), 0)
  440. self.assertEqual(len(project.get_service('data').containers(stopped=True)), 1)
  441. self.assertEqual(len(project.get_service('console').containers()), 0)
  442. def test_unscale_after_restart(self):
  443. web = self.create_service('web')
  444. project = Project('composetest', [web], self.client)
  445. project.start()
  446. service = project.get_service('web')
  447. service.scale(1)
  448. self.assertEqual(len(service.containers()), 1)
  449. service.scale(3)
  450. self.assertEqual(len(service.containers()), 3)
  451. project.up()
  452. service = project.get_service('web')
  453. self.assertEqual(len(service.containers()), 3)
  454. service.scale(1)
  455. self.assertEqual(len(service.containers()), 1)
  456. project.up()
  457. service = project.get_service('web')
  458. self.assertEqual(len(service.containers()), 1)
  459. # does scale=0 ,makes any sense? after recreating at least 1 container is running
  460. service.scale(0)
  461. project.up()
  462. service = project.get_service('web')
  463. self.assertEqual(len(service.containers()), 1)
  464. @v2_only()
  465. def test_project_up_networks(self):
  466. config_data = config.Config(
  467. version=2,
  468. services=[{
  469. 'name': 'web',
  470. 'image': 'busybox:latest',
  471. 'command': 'top',
  472. }],
  473. volumes={},
  474. networks={
  475. 'foo': {'driver': 'bridge'},
  476. 'bar': {'driver': None},
  477. 'baz': {},
  478. },
  479. )
  480. project = Project.from_config(
  481. client=self.client,
  482. name='composetest',
  483. config_data=config_data,
  484. )
  485. project.up()
  486. self.assertEqual(len(project.containers()), 1)
  487. for net_name in ['foo', 'bar', 'baz']:
  488. full_net_name = 'composetest_{}'.format(net_name)
  489. network_data = self.client.inspect_network(full_net_name)
  490. self.assertEqual(network_data['Name'], full_net_name)
  491. foo_data = self.client.inspect_network('composetest_foo')
  492. self.assertEqual(foo_data['Driver'], 'bridge')
  493. @v2_only()
  494. def test_up_with_ipam_config(self):
  495. config_data = config.Config(
  496. version=2,
  497. services=[],
  498. volumes={},
  499. networks={
  500. 'front': {
  501. 'driver': 'bridge',
  502. 'driver_opts': {
  503. "com.docker.network.bridge.enable_icc": "false",
  504. },
  505. 'ipam': {
  506. 'driver': 'default',
  507. 'config': [{
  508. "subnet": "172.28.0.0/16",
  509. "ip_range": "172.28.5.0/24",
  510. "gateway": "172.28.5.254",
  511. "aux_addresses": {
  512. "a": "172.28.1.5",
  513. "b": "172.28.1.6",
  514. "c": "172.28.1.7",
  515. },
  516. }],
  517. },
  518. },
  519. },
  520. )
  521. project = Project.from_config(
  522. client=self.client,
  523. name='composetest',
  524. config_data=config_data,
  525. )
  526. project.up()
  527. network = self.client.networks(names=['composetest_front'])[0]
  528. assert network['Options'] == {
  529. "com.docker.network.bridge.enable_icc": "false"
  530. }
  531. assert network['IPAM'] == {
  532. 'Driver': 'default',
  533. 'Options': None,
  534. 'Config': [{
  535. 'Subnet': "172.28.0.0/16",
  536. 'IPRange': "172.28.5.0/24",
  537. 'Gateway': "172.28.5.254",
  538. 'AuxiliaryAddresses': {
  539. 'a': '172.28.1.5',
  540. 'b': '172.28.1.6',
  541. 'c': '172.28.1.7',
  542. },
  543. }],
  544. }
  545. @v2_only()
  546. def test_project_up_volumes(self):
  547. vol_name = '{0:x}'.format(random.getrandbits(32))
  548. full_vol_name = 'composetest_{0}'.format(vol_name)
  549. config_data = config.Config(
  550. version=2,
  551. services=[{
  552. 'name': 'web',
  553. 'image': 'busybox:latest',
  554. 'command': 'top'
  555. }],
  556. volumes={vol_name: {'driver': 'local'}},
  557. networks={},
  558. )
  559. project = Project.from_config(
  560. name='composetest',
  561. config_data=config_data, client=self.client
  562. )
  563. project.up()
  564. self.assertEqual(len(project.containers()), 1)
  565. volume_data = self.client.inspect_volume(full_vol_name)
  566. self.assertEqual(volume_data['Name'], full_vol_name)
  567. self.assertEqual(volume_data['Driver'], 'local')
  568. @v2_only()
  569. def test_project_up_logging_with_multiple_files(self):
  570. base_file = config.ConfigFile(
  571. 'base.yml',
  572. {
  573. 'version': 2,
  574. 'services': {
  575. 'simple': {'image': 'busybox:latest', 'command': 'top'},
  576. 'another': {
  577. 'image': 'busybox:latest',
  578. 'command': 'top',
  579. 'logging': {
  580. 'driver': "json-file",
  581. 'options': {
  582. 'max-size': "10m"
  583. }
  584. }
  585. }
  586. }
  587. })
  588. override_file = config.ConfigFile(
  589. 'override.yml',
  590. {
  591. 'version': 2,
  592. 'services': {
  593. 'another': {
  594. 'logging': {
  595. 'driver': "none"
  596. }
  597. }
  598. }
  599. })
  600. details = config.ConfigDetails('.', [base_file, override_file])
  601. tmpdir = py.test.ensuretemp('logging_test')
  602. self.addCleanup(tmpdir.remove)
  603. with tmpdir.as_cwd():
  604. config_data = config.load(details)
  605. project = Project.from_config(
  606. name='composetest', config_data=config_data, client=self.client
  607. )
  608. project.up()
  609. containers = project.containers()
  610. self.assertEqual(len(containers), 2)
  611. another = project.get_service('another').containers()[0]
  612. log_config = another.get('HostConfig.LogConfig')
  613. self.assertTrue(log_config)
  614. self.assertEqual(log_config.get('Type'), 'none')
  615. @v2_only()
  616. def test_initialize_volumes(self):
  617. vol_name = '{0:x}'.format(random.getrandbits(32))
  618. full_vol_name = 'composetest_{0}'.format(vol_name)
  619. config_data = config.Config(
  620. version=2,
  621. services=[{
  622. 'name': 'web',
  623. 'image': 'busybox:latest',
  624. 'command': 'top'
  625. }],
  626. volumes={vol_name: {}},
  627. networks={},
  628. )
  629. project = Project.from_config(
  630. name='composetest',
  631. config_data=config_data, client=self.client
  632. )
  633. project.initialize_volumes()
  634. volume_data = self.client.inspect_volume(full_vol_name)
  635. self.assertEqual(volume_data['Name'], full_vol_name)
  636. self.assertEqual(volume_data['Driver'], 'local')
  637. @v2_only()
  638. def test_project_up_implicit_volume_driver(self):
  639. vol_name = '{0:x}'.format(random.getrandbits(32))
  640. full_vol_name = 'composetest_{0}'.format(vol_name)
  641. config_data = config.Config(
  642. version=2,
  643. services=[{
  644. 'name': 'web',
  645. 'image': 'busybox:latest',
  646. 'command': 'top'
  647. }],
  648. volumes={vol_name: {}},
  649. networks={},
  650. )
  651. project = Project.from_config(
  652. name='composetest',
  653. config_data=config_data, client=self.client
  654. )
  655. project.up()
  656. volume_data = self.client.inspect_volume(full_vol_name)
  657. self.assertEqual(volume_data['Name'], full_vol_name)
  658. self.assertEqual(volume_data['Driver'], 'local')
  659. @v2_only()
  660. def test_initialize_volumes_invalid_volume_driver(self):
  661. vol_name = '{0:x}'.format(random.getrandbits(32))
  662. config_data = config.Config(
  663. version=2,
  664. services=[{
  665. 'name': 'web',
  666. 'image': 'busybox:latest',
  667. 'command': 'top'
  668. }],
  669. volumes={vol_name: {'driver': 'foobar'}},
  670. networks={},
  671. )
  672. project = Project.from_config(
  673. name='composetest',
  674. config_data=config_data, client=self.client
  675. )
  676. with self.assertRaises(config.ConfigurationError):
  677. project.initialize_volumes()
  678. @v2_only()
  679. def test_initialize_volumes_updated_driver(self):
  680. vol_name = '{0:x}'.format(random.getrandbits(32))
  681. full_vol_name = 'composetest_{0}'.format(vol_name)
  682. config_data = config.Config(
  683. version=2,
  684. services=[{
  685. 'name': 'web',
  686. 'image': 'busybox:latest',
  687. 'command': 'top'
  688. }],
  689. volumes={vol_name: {'driver': 'local'}},
  690. networks={},
  691. )
  692. project = Project.from_config(
  693. name='composetest',
  694. config_data=config_data, client=self.client
  695. )
  696. project.initialize_volumes()
  697. volume_data = self.client.inspect_volume(full_vol_name)
  698. self.assertEqual(volume_data['Name'], full_vol_name)
  699. self.assertEqual(volume_data['Driver'], 'local')
  700. config_data = config_data._replace(
  701. volumes={vol_name: {'driver': 'smb'}}
  702. )
  703. project = Project.from_config(
  704. name='composetest',
  705. config_data=config_data, client=self.client
  706. )
  707. with self.assertRaises(config.ConfigurationError) as e:
  708. project.initialize_volumes()
  709. assert 'Configuration for volume {0} specifies driver smb'.format(
  710. vol_name
  711. ) in str(e.exception)
  712. @v2_only()
  713. def test_initialize_volumes_external_volumes(self):
  714. # Use composetest_ prefix so it gets garbage-collected in tearDown()
  715. vol_name = 'composetest_{0:x}'.format(random.getrandbits(32))
  716. full_vol_name = 'composetest_{0}'.format(vol_name)
  717. self.client.create_volume(vol_name)
  718. config_data = config.Config(
  719. version=2,
  720. services=[{
  721. 'name': 'web',
  722. 'image': 'busybox:latest',
  723. 'command': 'top'
  724. }],
  725. volumes={
  726. vol_name: {'external': True, 'external_name': vol_name}
  727. },
  728. networks=None,
  729. )
  730. project = Project.from_config(
  731. name='composetest',
  732. config_data=config_data, client=self.client
  733. )
  734. project.initialize_volumes()
  735. with self.assertRaises(NotFound):
  736. self.client.inspect_volume(full_vol_name)
  737. @v2_only()
  738. def test_initialize_volumes_inexistent_external_volume(self):
  739. vol_name = '{0:x}'.format(random.getrandbits(32))
  740. config_data = config.Config(
  741. version=2,
  742. services=[{
  743. 'name': 'web',
  744. 'image': 'busybox:latest',
  745. 'command': 'top'
  746. }],
  747. volumes={
  748. vol_name: {'external': True, 'external_name': vol_name}
  749. },
  750. networks=None,
  751. )
  752. project = Project.from_config(
  753. name='composetest',
  754. config_data=config_data, client=self.client
  755. )
  756. with self.assertRaises(config.ConfigurationError) as e:
  757. project.initialize_volumes()
  758. assert 'Volume {0} declared as external'.format(
  759. vol_name
  760. ) in str(e.exception)
  761. @v2_only()
  762. def test_project_up_named_volumes_in_binds(self):
  763. vol_name = '{0:x}'.format(random.getrandbits(32))
  764. full_vol_name = 'composetest_{0}'.format(vol_name)
  765. base_file = config.ConfigFile(
  766. 'base.yml',
  767. {
  768. 'version': 2,
  769. 'services': {
  770. 'simple': {
  771. 'image': 'busybox:latest',
  772. 'command': 'top',
  773. 'volumes': ['{0}:/data'.format(vol_name)]
  774. },
  775. },
  776. 'volumes': {
  777. vol_name: {'driver': 'local'}
  778. }
  779. })
  780. config_details = config.ConfigDetails('.', [base_file])
  781. config_data = config.load(config_details)
  782. project = Project.from_config(
  783. name='composetest', config_data=config_data, client=self.client
  784. )
  785. service = project.services[0]
  786. self.assertEqual(service.name, 'simple')
  787. volumes = service.options.get('volumes')
  788. self.assertEqual(len(volumes), 1)
  789. self.assertEqual(volumes[0].external, full_vol_name)
  790. project.up()
  791. engine_volumes = self.client.volumes()['Volumes']
  792. container = service.get_container()
  793. assert [mount['Name'] for mount in container.get('Mounts')] == [full_vol_name]
  794. assert next((v for v in engine_volumes if v['Name'] == vol_name), None) is None