202 lines
5.5 KiB
Python
202 lines
5.5 KiB
Python
from abc import ABC, abstractmethod
|
|
from typing import Dict, Optional, Protocol
|
|
|
|
from app.config import SandboxSettings
|
|
from app.sandbox.core.sandbox import DockerSandbox
|
|
|
|
|
|
class SandboxFileOperations(Protocol):
|
|
"""Protocol for sandbox file operations."""
|
|
|
|
async def copy_from(self, container_path: str, local_path: str) -> None:
|
|
"""Copies file from container to local.
|
|
|
|
Args:
|
|
container_path: File path in container.
|
|
local_path: Local destination path.
|
|
"""
|
|
...
|
|
|
|
async def copy_to(self, local_path: str, container_path: str) -> None:
|
|
"""Copies file from local to container.
|
|
|
|
Args:
|
|
local_path: Local source file path.
|
|
container_path: Destination path in container.
|
|
"""
|
|
...
|
|
|
|
async def read_file(self, path: str) -> str:
|
|
"""Reads file content from container.
|
|
|
|
Args:
|
|
path: File path in container.
|
|
|
|
Returns:
|
|
str: File content.
|
|
"""
|
|
...
|
|
|
|
async def write_file(self, path: str, content: str) -> None:
|
|
"""Writes content to file in container.
|
|
|
|
Args:
|
|
path: File path in container.
|
|
content: Content to write.
|
|
"""
|
|
...
|
|
|
|
|
|
class BaseSandboxClient(ABC):
|
|
"""Base sandbox client interface."""
|
|
|
|
@abstractmethod
|
|
async def create(
|
|
self,
|
|
config: Optional[SandboxSettings] = None,
|
|
volume_bindings: Optional[Dict[str, str]] = None,
|
|
) -> None:
|
|
"""Creates sandbox."""
|
|
|
|
@abstractmethod
|
|
async def run_command(self, command: str, timeout: Optional[int] = None) -> str:
|
|
"""Executes command."""
|
|
|
|
@abstractmethod
|
|
async def copy_from(self, container_path: str, local_path: str) -> None:
|
|
"""Copies file from container."""
|
|
|
|
@abstractmethod
|
|
async def copy_to(self, local_path: str, container_path: str) -> None:
|
|
"""Copies file to container."""
|
|
|
|
@abstractmethod
|
|
async def read_file(self, path: str) -> str:
|
|
"""Reads file."""
|
|
|
|
@abstractmethod
|
|
async def write_file(self, path: str, content: str) -> None:
|
|
"""Writes file."""
|
|
|
|
@abstractmethod
|
|
async def cleanup(self) -> None:
|
|
"""Cleans up resources."""
|
|
|
|
|
|
class LocalSandboxClient(BaseSandboxClient):
|
|
"""Local sandbox client implementation."""
|
|
|
|
def __init__(self):
|
|
"""Initializes local sandbox client."""
|
|
self.sandbox: Optional[DockerSandbox] = None
|
|
|
|
async def create(
|
|
self,
|
|
config: Optional[SandboxSettings] = None,
|
|
volume_bindings: Optional[Dict[str, str]] = None,
|
|
) -> None:
|
|
"""Creates a sandbox.
|
|
|
|
Args:
|
|
config: Sandbox configuration.
|
|
volume_bindings: Volume mappings.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox creation fails.
|
|
"""
|
|
self.sandbox = DockerSandbox(config, volume_bindings)
|
|
await self.sandbox.create()
|
|
|
|
async def run_command(self, command: str, timeout: Optional[int] = None) -> str:
|
|
"""Runs command in sandbox.
|
|
|
|
Args:
|
|
command: Command to execute.
|
|
timeout: Execution timeout in seconds.
|
|
|
|
Returns:
|
|
Command output.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox not initialized.
|
|
"""
|
|
if not self.sandbox:
|
|
raise RuntimeError("Sandbox not initialized")
|
|
return await self.sandbox.run_command(command, timeout)
|
|
|
|
async def copy_from(self, container_path: str, local_path: str) -> None:
|
|
"""Copies file from container to local.
|
|
|
|
Args:
|
|
container_path: File path in container.
|
|
local_path: Local destination path.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox not initialized.
|
|
"""
|
|
if not self.sandbox:
|
|
raise RuntimeError("Sandbox not initialized")
|
|
await self.sandbox.copy_from(container_path, local_path)
|
|
|
|
async def copy_to(self, local_path: str, container_path: str) -> None:
|
|
"""Copies file from local to container.
|
|
|
|
Args:
|
|
local_path: Local source file path.
|
|
container_path: Destination path in container.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox not initialized.
|
|
"""
|
|
if not self.sandbox:
|
|
raise RuntimeError("Sandbox not initialized")
|
|
await self.sandbox.copy_to(local_path, container_path)
|
|
|
|
async def read_file(self, path: str) -> str:
|
|
"""Reads file from container.
|
|
|
|
Args:
|
|
path: File path in container.
|
|
|
|
Returns:
|
|
File content.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox not initialized.
|
|
"""
|
|
if not self.sandbox:
|
|
raise RuntimeError("Sandbox not initialized")
|
|
return await self.sandbox.read_file(path)
|
|
|
|
async def write_file(self, path: str, content: str) -> None:
|
|
"""Writes file to container.
|
|
|
|
Args:
|
|
path: File path in container.
|
|
content: File content.
|
|
|
|
Raises:
|
|
RuntimeError: If sandbox not initialized.
|
|
"""
|
|
if not self.sandbox:
|
|
raise RuntimeError("Sandbox not initialized")
|
|
await self.sandbox.write_file(path, content)
|
|
|
|
async def cleanup(self) -> None:
|
|
"""Cleans up resources."""
|
|
if self.sandbox:
|
|
await self.sandbox.cleanup()
|
|
self.sandbox = None
|
|
|
|
|
|
def create_sandbox_client() -> LocalSandboxClient:
|
|
"""Creates a sandbox client.
|
|
|
|
Returns:
|
|
LocalSandboxClient: Sandbox client instance.
|
|
"""
|
|
return LocalSandboxClient()
|
|
|
|
|
|
SANDBOX_CLIENT = create_sandbox_client()
|