test_runtime_build.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import os
  2. import tarfile
  3. import tempfile
  4. from importlib.metadata import version
  5. from unittest.mock import MagicMock, patch
  6. import pytest
  7. import toml
  8. from opendevin.runtime.utils.runtime_build import (
  9. _generate_dockerfile,
  10. _get_new_image_name,
  11. _put_source_code_to_dir,
  12. build_runtime_image,
  13. )
  14. RUNTIME_IMAGE_PREFIX = 'od_runtime'
  15. @pytest.fixture
  16. def temp_dir():
  17. with tempfile.TemporaryDirectory() as temp_dir:
  18. yield temp_dir
  19. def test_put_source_code_to_dir(temp_dir):
  20. folder_name = _put_source_code_to_dir(temp_dir)
  21. # assert there is a file called 'project.tar.gz' in the temp_dir
  22. assert os.path.exists(os.path.join(temp_dir, 'project.tar.gz'))
  23. # untar the file
  24. with tarfile.open(os.path.join(temp_dir, 'project.tar.gz'), 'r:gz') as tar:
  25. tar.extractall(path=temp_dir)
  26. # check the source file is the same as the current code base
  27. assert os.path.exists(os.path.join(temp_dir, folder_name, 'pyproject.toml'))
  28. # make sure the version from the pyproject.toml is the same as the current version
  29. with open(os.path.join(temp_dir, folder_name, 'pyproject.toml'), 'r') as f:
  30. pyproject = toml.load(f)
  31. _pyproject_version = pyproject['tool']['poetry']['version']
  32. assert _pyproject_version == version('opendevin')
  33. def test_generate_dockerfile_scratch():
  34. base_image = 'debian:11'
  35. source_code_dirname = 'dummy'
  36. dockerfile_content = _generate_dockerfile(
  37. base_image,
  38. source_code_dirname=source_code_dirname,
  39. skip_init=False,
  40. )
  41. assert base_image in dockerfile_content
  42. assert 'RUN apt update && apt install -y wget sudo' in dockerfile_content
  43. assert (
  44. 'RUN /opendevin/miniforge3/bin/mamba install conda-forge::poetry -y'
  45. in dockerfile_content
  46. )
  47. # Check the update command
  48. assert (
  49. f'RUN mv /opendevin/{source_code_dirname} /opendevin/code' in dockerfile_content
  50. )
  51. assert (
  52. '/opendevin/miniforge3/bin/mamba run -n base poetry install'
  53. in dockerfile_content
  54. )
  55. def test_generate_dockerfile_skip_init():
  56. base_image = 'debian:11'
  57. source_code_dirname = 'dummy'
  58. dockerfile_content = _generate_dockerfile(
  59. base_image,
  60. source_code_dirname=source_code_dirname,
  61. skip_init=True,
  62. )
  63. # These commands SHOULD NOT include in the dockerfile if skip_init is True
  64. assert 'RUN apt update && apt install -y wget sudo' not in dockerfile_content
  65. assert (
  66. 'RUN /opendevin/miniforge3/bin/mamba install conda-forge::poetry -y'
  67. not in dockerfile_content
  68. )
  69. # These update commands SHOULD still in the dockerfile
  70. assert (
  71. f'RUN mv /opendevin/{source_code_dirname} /opendevin/code' in dockerfile_content
  72. )
  73. assert (
  74. '/opendevin/miniforge3/bin/mamba run -n base poetry install'
  75. in dockerfile_content
  76. )
  77. def test_get_new_image_name_eventstream():
  78. base_image = 'debian:11'
  79. new_image_name = _get_new_image_name(base_image)
  80. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
  81. base_image = 'ubuntu:22.04'
  82. new_image_name = _get_new_image_name(base_image)
  83. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_22.04'
  84. base_image = 'ubuntu'
  85. new_image_name = _get_new_image_name(base_image)
  86. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_latest'
  87. def test_get_new_image_name_eventstream_dev_mode():
  88. base_image = f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
  89. new_image_name = _get_new_image_name(base_image, dev_mode=True)
  90. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:debian_tag_11'
  91. base_image = f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_22.04'
  92. new_image_name = _get_new_image_name(base_image, dev_mode=True)
  93. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:ubuntu_tag_22.04'
  94. base_image = f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_latest'
  95. new_image_name = _get_new_image_name(base_image, dev_mode=True)
  96. assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:ubuntu_tag_latest'
  97. def test_get_new_image_name_eventstream_dev_invalid_base_image():
  98. with pytest.raises(ValueError):
  99. base_image = 'debian:11'
  100. _get_new_image_name(base_image, dev_mode=True)
  101. with pytest.raises(ValueError):
  102. base_image = 'ubuntu:22.04'
  103. _get_new_image_name(base_image, dev_mode=True)
  104. with pytest.raises(ValueError):
  105. base_image = 'ubuntu:latest'
  106. _get_new_image_name(base_image, dev_mode=True)
  107. @patch('opendevin.runtime.utils.runtime_build._build_sandbox_image')
  108. @patch('opendevin.runtime.utils.runtime_build.docker.DockerClient')
  109. def test_build_runtime_image_from_scratch(mock_docker_client, mock_build_sandbox_image):
  110. base_image = 'debian:11'
  111. mock_docker_client.images.list.return_value = []
  112. image_name = build_runtime_image(base_image, mock_docker_client)
  113. assert image_name == f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
  114. mock_build_sandbox_image.assert_called_once_with(
  115. base_image,
  116. f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11',
  117. mock_docker_client,
  118. skip_init=False,
  119. )
  120. @patch('opendevin.runtime.utils.runtime_build._build_sandbox_image')
  121. @patch('opendevin.runtime.utils.runtime_build.docker.DockerClient')
  122. def test_build_runtime_image_exist_no_update_source(
  123. mock_docker_client, mock_build_sandbox_image
  124. ):
  125. base_image = 'debian:11'
  126. mock_docker_client.images.list.return_value = [
  127. MagicMock(tags=[f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'])
  128. ]
  129. image_name = build_runtime_image(base_image, mock_docker_client)
  130. assert image_name == f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
  131. mock_build_sandbox_image.assert_not_called()
  132. @patch('opendevin.runtime.utils.runtime_build._build_sandbox_image')
  133. @patch('opendevin.runtime.utils.runtime_build.docker.DockerClient')
  134. def test_build_runtime_image_exist_with_update_source(
  135. mock_docker_client, mock_build_sandbox_image
  136. ):
  137. base_image = 'debian:11'
  138. mock_docker_client.images.list.return_value = [
  139. MagicMock(tags=[f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'])
  140. ]
  141. image_name = build_runtime_image(
  142. base_image, mock_docker_client, update_source_code=True
  143. )
  144. assert image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:debian_tag_11'
  145. mock_build_sandbox_image.assert_called_once_with(
  146. f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11',
  147. f'{RUNTIME_IMAGE_PREFIX}_dev:debian_tag_11',
  148. mock_docker_client,
  149. skip_init=True,
  150. )