feat(sandbox): add support for extra Docker build arguments (#5447)

This commit is contained in:
Cheng Yang
2024-12-12 10:21:46 +08:00
committed by GitHub
parent ffd472d6b8
commit 7e4c1c733b
8 changed files with 30 additions and 3 deletions

View File

@@ -217,6 +217,9 @@ llm_config = 'gpt3'
# Use host network # Use host network
#use_host_network = false #use_host_network = false
# runtime extra build args
#runtime_extra_build_args = ["--network=host", "--add-host=host.docker.internal:host-gateway"]
# Enable auto linting after editing # Enable auto linting after editing
#enable_auto_lint = false #enable_auto_lint = false

View File

@@ -48,6 +48,7 @@ class SandboxConfig:
False # once enabled, OpenHands would lint files after editing False # once enabled, OpenHands would lint files after editing
) )
use_host_network: bool = False use_host_network: bool = False
runtime_extra_build_args: list[str] | None = None
initialize_plugins: bool = True initialize_plugins: bool = True
force_rebuild_runtime: bool = False force_rebuild_runtime: bool = False
runtime_extra_deps: str | None = None runtime_extra_deps: str | None = None

View File

@@ -8,6 +8,7 @@ class RuntimeBuilder(abc.ABC):
path: str, path: str,
tags: list[str], tags: list[str],
platform: str | None = None, platform: str | None = None,
extra_build_args: list[str] | None = None,
) -> str: ) -> str:
"""Build the runtime image. """Build the runtime image.
@@ -15,6 +16,7 @@ class RuntimeBuilder(abc.ABC):
path (str): The path to the runtime image's build directory. path (str): The path to the runtime image's build directory.
tags (list[str]): The tags to apply to the runtime image (e.g., ["repo:my-repo", "sha:my-sha"]). tags (list[str]): The tags to apply to the runtime image (e.g., ["repo:my-repo", "sha:my-sha"]).
platform (str, optional): The target platform for the build. Defaults to None. platform (str, optional): The target platform for the build. Defaults to None.
extra_build_args (list[str], optional): Additional build arguments to pass to the builder. Defaults to None.
Returns: Returns:
str: The name:tag of the runtime image after build (e.g., "repo:sha"). str: The name:tag of the runtime image after build (e.g., "repo:sha").

View File

@@ -28,8 +28,8 @@ class DockerRuntimeBuilder(RuntimeBuilder):
path: str, path: str,
tags: list[str], tags: list[str],
platform: str | None = None, platform: str | None = None,
use_local_cache: bool = False,
extra_build_args: list[str] | None = None, extra_build_args: list[str] | None = None,
use_local_cache: bool = False,
) -> str: ) -> str:
"""Builds a Docker image using BuildKit and handles the build logs appropriately. """Builds a Docker image using BuildKit and handles the build logs appropriately.

View File

@@ -23,7 +23,13 @@ class RemoteRuntimeBuilder(RuntimeBuilder):
self.session = requests.Session() self.session = requests.Session()
self.session.headers.update({'X-API-Key': self.api_key}) self.session.headers.update({'X-API-Key': self.api_key})
def build(self, path: str, tags: list[str], platform: str | None = None) -> str: def build(
self,
path: str,
tags: list[str],
platform: str | None = None,
extra_build_args: list[str] | None = None,
) -> str:
"""Builds a Docker image using the Runtime API's /build endpoint.""" """Builds a Docker image using the Runtime API's /build endpoint."""
# Create a tar archive of the build context # Create a tar archive of the build context
tar_buffer = io.BytesIO() tar_buffer = io.BytesIO()

View File

@@ -229,6 +229,7 @@ class EventStreamRuntime(Runtime):
platform=self.config.sandbox.platform, platform=self.config.sandbox.platform,
extra_deps=self.config.sandbox.runtime_extra_deps, extra_deps=self.config.sandbox.runtime_extra_deps,
force_rebuild=self.config.sandbox.force_rebuild_runtime, force_rebuild=self.config.sandbox.force_rebuild_runtime,
extra_build_args=self.config.sandbox.runtime_extra_build_args,
) )
self.log( self.log(

View File

@@ -111,6 +111,7 @@ def build_runtime_image(
build_folder: str | None = None, build_folder: str | None = None,
dry_run: bool = False, dry_run: bool = False,
force_rebuild: bool = False, force_rebuild: bool = False,
extra_build_args: List[str] | None = None,
) -> str: ) -> str:
"""Prepares the final docker build folder. """Prepares the final docker build folder.
If dry_run is False, it will also build the OpenHands runtime Docker image using the docker build folder. If dry_run is False, it will also build the OpenHands runtime Docker image using the docker build folder.
@@ -123,6 +124,7 @@ def build_runtime_image(
- build_folder (str): The directory to use for the build. If not provided a temporary directory will be used - build_folder (str): The directory to use for the build. If not provided a temporary directory will be used
- dry_run (bool): if True, it will only ready the build folder. It will not actually build the Docker image - dry_run (bool): if True, it will only ready the build folder. It will not actually build the Docker image
- force_rebuild (bool): if True, it will create the Dockerfile which uses the base_image - force_rebuild (bool): if True, it will create the Dockerfile which uses the base_image
- extra_build_args (List[str]): Additional build arguments to pass to the builder
Returns: Returns:
- str: <image_repo>:<MD5 hash>. Where MD5 hash is the hash of the docker build folder - str: <image_repo>:<MD5 hash>. Where MD5 hash is the hash of the docker build folder
@@ -139,6 +141,7 @@ def build_runtime_image(
dry_run=dry_run, dry_run=dry_run,
force_rebuild=force_rebuild, force_rebuild=force_rebuild,
platform=platform, platform=platform,
extra_build_args=extra_build_args,
) )
return result return result
@@ -150,6 +153,7 @@ def build_runtime_image(
dry_run=dry_run, dry_run=dry_run,
force_rebuild=force_rebuild, force_rebuild=force_rebuild,
platform=platform, platform=platform,
extra_build_args=extra_build_args,
) )
return result return result
@@ -162,6 +166,7 @@ def build_runtime_image_in_folder(
dry_run: bool, dry_run: bool,
force_rebuild: bool, force_rebuild: bool,
platform: str | None = None, platform: str | None = None,
extra_build_args: List[str] | None = None,
) -> str: ) -> str:
runtime_image_repo, _ = get_runtime_image_repo_and_tag(base_image) runtime_image_repo, _ = get_runtime_image_repo_and_tag(base_image)
lock_tag = f'oh_v{oh_version}_{get_hash_for_lock_files(base_image)}' lock_tag = f'oh_v{oh_version}_{get_hash_for_lock_files(base_image)}'
@@ -193,6 +198,7 @@ def build_runtime_image_in_folder(
lock_tag, lock_tag,
versioned_tag, versioned_tag,
platform, platform,
extra_build_args=extra_build_args,
) )
return hash_image_name return hash_image_name
@@ -234,6 +240,7 @@ def build_runtime_image_in_folder(
if build_from == BuildFromImageType.SCRATCH if build_from == BuildFromImageType.SCRATCH
else None, else None,
platform=platform, platform=platform,
extra_build_args=extra_build_args,
) )
return hash_image_name return hash_image_name
@@ -339,6 +346,7 @@ def _build_sandbox_image(
lock_tag: str, lock_tag: str,
versioned_tag: str | None, versioned_tag: str | None,
platform: str | None = None, platform: str | None = None,
extra_build_args: List[str] | None = None,
): ):
"""Build and tag the sandbox image. The image will be tagged with all tags that do not yet exist""" """Build and tag the sandbox image. The image will be tagged with all tags that do not yet exist"""
names = [ names = [
@@ -350,7 +358,10 @@ def _build_sandbox_image(
names = [name for name in names if not runtime_builder.image_exists(name, False)] names = [name for name in names if not runtime_builder.image_exists(name, False)]
image_name = runtime_builder.build( image_name = runtime_builder.build(
path=str(build_folder), tags=names, platform=platform path=str(build_folder),
tags=names,
platform=platform,
extra_build_args=extra_build_args,
) )
if not image_name: if not image_name:
raise RuntimeError(f'Build failed for image {names}') raise RuntimeError(f'Build failed for image {names}')

View File

@@ -239,6 +239,7 @@ def test_build_runtime_image_from_scratch():
f'{get_runtime_image_repo()}:{OH_VERSION}_mock-versioned-tag', f'{get_runtime_image_repo()}:{OH_VERSION}_mock-versioned-tag',
], ],
platform=None, platform=None,
extra_build_args=None,
) )
assert ( assert (
image_name image_name
@@ -333,6 +334,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_exist():
# VERSION tag will NOT be included except from scratch # VERSION tag will NOT be included except from scratch
], ],
platform=None, platform=None,
extra_build_args=None,
) )
mock_prep_build_folder.assert_called_once_with( mock_prep_build_folder.assert_called_once_with(
ANY, ANY,
@@ -391,6 +393,7 @@ def test_build_runtime_image_exact_hash_not_exist_and_lock_not_exist_and_version
# VERSION tag will NOT be included except from scratch # VERSION tag will NOT be included except from scratch
], ],
platform=None, platform=None,
extra_build_args=None,
) )
mock_prep_build_folder.assert_called_once_with( mock_prep_build_folder.assert_called_once_with(
ANY, ANY,