bundle_test.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import docker
  4. import pytest
  5. from .. import mock
  6. from compose import bundle
  7. from compose import service
  8. from compose.cli.errors import UserError
  9. from compose.config.config import Config
  10. from compose.const import COMPOSEFILE_V2_0 as V2_0
  11. @pytest.fixture
  12. def mock_service():
  13. return mock.create_autospec(
  14. service.Service,
  15. client=mock.create_autospec(docker.APIClient),
  16. options={})
  17. def test_get_image_digest_exists(mock_service):
  18. mock_service.options['image'] = 'abcd'
  19. mock_service.image.return_value = {'RepoDigests': ['digest1']}
  20. digest = bundle.get_image_digest(mock_service)
  21. assert digest == 'digest1'
  22. def test_get_image_digest_image_uses_digest(mock_service):
  23. mock_service.options['image'] = image_id = 'redis@sha256:digest'
  24. digest = bundle.get_image_digest(mock_service)
  25. assert digest == image_id
  26. assert not mock_service.image.called
  27. def test_get_image_digest_no_image(mock_service):
  28. with pytest.raises(UserError) as exc:
  29. bundle.get_image_digest(service.Service(name='theservice'))
  30. assert "doesn't define an image tag" in exc.exconly()
  31. def test_push_image_with_saved_digest(mock_service):
  32. mock_service.options['build'] = '.'
  33. mock_service.options['image'] = image_id = 'abcd'
  34. mock_service.push.return_value = expected = 'sha256:thedigest'
  35. mock_service.image.return_value = {'RepoDigests': ['digest1']}
  36. digest = bundle.push_image(mock_service)
  37. assert digest == image_id + '@' + expected
  38. mock_service.push.assert_called_once_with()
  39. assert not mock_service.client.push.called
  40. def test_push_image(mock_service):
  41. mock_service.options['build'] = '.'
  42. mock_service.options['image'] = image_id = 'abcd'
  43. mock_service.push.return_value = expected = 'sha256:thedigest'
  44. mock_service.image.return_value = {'RepoDigests': []}
  45. digest = bundle.push_image(mock_service)
  46. assert digest == image_id + '@' + expected
  47. mock_service.push.assert_called_once_with()
  48. mock_service.client.pull.assert_called_once_with(digest)
  49. def test_to_bundle():
  50. image_digests = {'a': 'aaaa', 'b': 'bbbb'}
  51. services = [
  52. {'name': 'a', 'build': '.', },
  53. {'name': 'b', 'build': './b'},
  54. ]
  55. config = Config(
  56. version=V2_0,
  57. services=services,
  58. volumes={'special': {}},
  59. networks={'extra': {}},
  60. secrets={},
  61. configs={}
  62. )
  63. with mock.patch('compose.bundle.log.warn', autospec=True) as mock_log:
  64. output = bundle.to_bundle(config, image_digests)
  65. assert mock_log.mock_calls == [
  66. mock.call("Unsupported top level key 'networks' - ignoring"),
  67. mock.call("Unsupported top level key 'volumes' - ignoring"),
  68. ]
  69. assert output == {
  70. 'Version': '0.1',
  71. 'Services': {
  72. 'a': {'Image': 'aaaa', 'Networks': ['default']},
  73. 'b': {'Image': 'bbbb', 'Networks': ['default']},
  74. }
  75. }
  76. def test_convert_service_to_bundle():
  77. name = 'theservice'
  78. image_digest = 'thedigest'
  79. service_dict = {
  80. 'ports': ['80'],
  81. 'expose': ['1234'],
  82. 'networks': {'extra': {}},
  83. 'command': 'foo',
  84. 'entrypoint': 'entry',
  85. 'environment': {'BAZ': 'ENV'},
  86. 'build': '.',
  87. 'working_dir': '/tmp',
  88. 'user': 'root',
  89. 'labels': {'FOO': 'LABEL'},
  90. 'privileged': True,
  91. }
  92. with mock.patch('compose.bundle.log.warn', autospec=True) as mock_log:
  93. config = bundle.convert_service_to_bundle(name, service_dict, image_digest)
  94. mock_log.assert_called_once_with(
  95. "Unsupported key 'privileged' in services.theservice - ignoring")
  96. assert config == {
  97. 'Image': image_digest,
  98. 'Ports': [
  99. {'Protocol': 'tcp', 'Port': 80},
  100. {'Protocol': 'tcp', 'Port': 1234},
  101. ],
  102. 'Networks': ['extra'],
  103. 'Command': ['entry', 'foo'],
  104. 'Env': ['BAZ=ENV'],
  105. 'WorkingDir': '/tmp',
  106. 'User': 'root',
  107. 'Labels': {'FOO': 'LABEL'},
  108. }
  109. def test_set_command_and_args_none():
  110. config = {}
  111. bundle.set_command_and_args(config, [], [])
  112. assert config == {}
  113. def test_set_command_and_args_from_command():
  114. config = {}
  115. bundle.set_command_and_args(config, [], "echo ok")
  116. assert config == {'Args': ['echo', 'ok']}
  117. def test_set_command_and_args_from_entrypoint():
  118. config = {}
  119. bundle.set_command_and_args(config, "echo entry", [])
  120. assert config == {'Command': ['echo', 'entry']}
  121. def test_set_command_and_args_from_both():
  122. config = {}
  123. bundle.set_command_and_args(config, "echo entry", ["extra", "arg"])
  124. assert config == {'Command': ['echo', 'entry', "extra", "arg"]}
  125. def test_make_service_networks_default():
  126. name = 'theservice'
  127. service_dict = {}
  128. with mock.patch('compose.bundle.log.warn', autospec=True) as mock_log:
  129. networks = bundle.make_service_networks(name, service_dict)
  130. assert not mock_log.called
  131. assert networks == ['default']
  132. def test_make_service_networks():
  133. name = 'theservice'
  134. service_dict = {
  135. 'networks': {
  136. 'foo': {
  137. 'aliases': ['one', 'two'],
  138. },
  139. 'bar': {}
  140. },
  141. }
  142. with mock.patch('compose.bundle.log.warn', autospec=True) as mock_log:
  143. networks = bundle.make_service_networks(name, service_dict)
  144. mock_log.assert_called_once_with(
  145. "Unsupported key 'aliases' in services.theservice.networks.foo - ignoring")
  146. assert sorted(networks) == sorted(service_dict['networks'])
  147. def test_make_port_specs():
  148. service_dict = {
  149. 'expose': ['80', '500/udp'],
  150. 'ports': [
  151. '400:80',
  152. '222',
  153. '127.0.0.1:8001:8001',
  154. '127.0.0.1:5000-5001:3000-3001'],
  155. }
  156. port_specs = bundle.make_port_specs(service_dict)
  157. assert port_specs == [
  158. {'Protocol': 'tcp', 'Port': 80},
  159. {'Protocol': 'tcp', 'Port': 222},
  160. {'Protocol': 'tcp', 'Port': 8001},
  161. {'Protocol': 'tcp', 'Port': 3000},
  162. {'Protocol': 'tcp', 'Port': 3001},
  163. {'Protocol': 'udp', 'Port': 500},
  164. ]
  165. def test_make_port_spec_with_protocol():
  166. port_spec = bundle.make_port_spec("5000/udp")
  167. assert port_spec == {'Protocol': 'udp', 'Port': 5000}
  168. def test_make_port_spec_default_protocol():
  169. port_spec = bundle.make_port_spec("50000")
  170. assert port_spec == {'Protocol': 'tcp', 'Port': 50000}