Copy files from one Docker container to another via a temporary local directory. Be extremely careful with trailing slashes in src_path and dest_path, the behavior of docker cp is also different depending on whether the destination exists.
(
src_container: DockerEnvironment,
dest_container: DockerEnvironment,
src_path: str | Path,
dest_path: str | Path,
)
| 43 | |
| 44 | def execute(self, action: str | dict, cwd: str = "", *, timeout: int | None = None) -> dict: |
| 45 | if isinstance(action, str): |
| 46 | action = {"command": action} |
| 47 | result = super().execute(action, cwd, timeout=timeout) |
| 48 | if isinstance(result, dict) and result.get("output"): |
| 49 | result["output"] = redact_secrets(result["output"]) |
| 50 | return result |
| 51 | |
| 52 | |
| 53 | def assert_zero_exit_code(result: dict, *, logger: logging.Logger | None = None) -> dict: |
| 54 | if result.get("returncode", 0) != 0: |
| 55 | msg = f"Command failed with exit code {result.get('returncode')}:\n{redact_secrets(result.get('output'))}" |
| 56 | if logger is not None: |
| 57 | logger.error(msg) |
| 58 | raise RuntimeError(msg) |
| 59 | return result |
| 60 | |
| 61 | |
| 62 | def copy_between_containers( |
| 63 | src_container: DockerEnvironment, |
| 64 | dest_container: DockerEnvironment, |
| 65 | src_path: str | Path, |
| 66 | dest_path: str | Path, |
| 67 | ): |
| 68 | """ |
| 69 | Copy files from one Docker container to another via a temporary local directory. |
| 70 | |
| 71 | Be extremely careful with trailing slashes in src_path and dest_path, the behavior |
| 72 | of docker cp is also different depending on whether the destination exists. |
| 73 | """ |
| 74 | print( |
| 75 | f"Copy between containers: {src_container.container_id}:{src_path} -> {dest_container.container_id}:{dest_path}" |
| 76 | ) |
| 77 | with tempfile.TemporaryDirectory(dir=_scratch_dir()) as temp_dir: |
| 78 | temp_path = Path(temp_dir) / Path(src_path).name |
| 79 | |
| 80 | # Copy from source container to temporary local directory |
| 81 | cmd_src = [ |
| 82 | "docker", |
| 83 | "cp", |
| 84 | f"{src_container.container_id}:{src_path}", |
| 85 | str(temp_path), |
| 86 | ] |
| 87 | result_src = subprocess.run(cmd_src, check=False, capture_output=True, text=True) |
| 88 | if result_src.returncode != 0: |
| 89 | raise RuntimeError( |
| 90 | f"Failed to copy from {src_container.container_id} to local temp: {result_src.stdout}{result_src.stderr}" |
| 91 | ) |
| 92 | |
| 93 | # Remove excluded patterns |
| 94 | for pattern in COPY_EXCLUDE_PATTERNS: |
| 95 | excluded_path = temp_path / pattern |
| 96 | if excluded_path.exists(): |
| 97 | if excluded_path.is_dir(): |
| 98 | shutil.rmtree(excluded_path) |
| 99 | else: |
| 100 | excluded_path.unlink() |
| 101 | |
| 102 | # Ensure destination folder exists |
no test coverage detected