docker.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import docker
  2. from openhands.core.logger import openhands_logger as logger
  3. from openhands.runtime.builder.base import RuntimeBuilder
  4. class DockerRuntimeBuilder(RuntimeBuilder):
  5. def __init__(self, docker_client: docker.DockerClient):
  6. self.docker_client = docker_client
  7. def build(self, path: str, tags: list[str]) -> str:
  8. target_image_hash_name = tags[0]
  9. target_image_repo, target_image_hash_tag = target_image_hash_name.split(':')
  10. target_image_tag = tags[1].split(':')[1] if len(tags) > 1 else None
  11. try:
  12. build_logs = self.docker_client.api.build(
  13. path=path,
  14. tag=target_image_hash_name,
  15. rm=True,
  16. decode=True,
  17. )
  18. except docker.errors.BuildError as e:
  19. logger.error(f'Sandbox image build failed: {e}')
  20. raise RuntimeError(f'Sandbox image build failed: {e}')
  21. for log in build_logs:
  22. if 'stream' in log:
  23. logger.info(log['stream'].strip())
  24. elif 'error' in log:
  25. logger.error(log['error'].strip())
  26. else:
  27. logger.info(str(log))
  28. logger.info(f'Image [{target_image_hash_name}] build finished.')
  29. assert (
  30. target_image_tag
  31. ), f'Expected target image tag [{target_image_tag}] is None'
  32. image = self.docker_client.images.get(target_image_hash_name)
  33. image.tag(target_image_repo, target_image_tag)
  34. logger.info(
  35. f'Re-tagged image [{target_image_hash_name}] with more generic tag [{target_image_tag}]'
  36. )
  37. # Check if the image is built successfully
  38. image = self.docker_client.images.get(target_image_hash_name)
  39. if image is None:
  40. raise RuntimeError(
  41. f'Build failed: Image {target_image_hash_name} not found'
  42. )
  43. tags_str = (
  44. f'{target_image_hash_tag}, {target_image_tag}'
  45. if target_image_tag
  46. else target_image_hash_tag
  47. )
  48. logger.info(
  49. f'Image {target_image_repo} with tags [{tags_str}] built successfully'
  50. )
  51. return target_image_hash_name
  52. def image_exists(self, image_name: str) -> bool:
  53. """Check if the image exists in the registry (try to pull it first) or in the local store.
  54. Args:
  55. image_name (str): The Docker image to check (<image repo>:<image tag>)
  56. Returns:
  57. bool: Whether the Docker image exists in the registry or in the local store
  58. """
  59. try:
  60. logger.info(f'Checking, if image {image_name} exists locally.')
  61. self.docker_client.images.get(image_name)
  62. logger.info(f'Image {image_name} found locally.')
  63. return True
  64. except docker.errors.ImageNotFound:
  65. try:
  66. logger.info(
  67. 'Image not found locally. Trying to pull it, please wait...'
  68. )
  69. self.docker_client.images.pull(image_name)
  70. logger.info(f'Image {image_name} pulled successfully.')
  71. return True
  72. except docker.errors.ImageNotFound:
  73. logger.info('Could not find image locally or in registry.')
  74. return False
  75. except Exception:
  76. logger.info('Could not pull image directly.')
  77. return False