DEVELOPMENT.md
1 # Development Guide 2 3 This guide provides comprehensive information for developers working on OpenSandbox Server, including environment setup, architecture deep-dive, testing strategies, and contribution workflows. 4 5 ## 📋 Table of Contents 6 7 - [Development Environment Setup](#development-environment-setup) 8 - [Project Structure](#project-structure) 9 - [Architecture Deep Dive](#architecture-deep-dive) 10 - [Development Workflow](#development-workflow) 11 - [Testing Guide](#testing-guide) 12 - [Working with Docker Runtime](#working-with-docker-runtime) 13 - [Working with Kubernetes Runtime](#working-with-kubernetes-runtime) 14 - [Code Style and Standards](#code-style-and-standards) 15 - [Debugging](#debugging) 16 - [Performance Optimization](#performance-optimization) 17 - [Contributing](#contributing) 18 19 ## Development Environment Setup 20 21 ### Prerequisites 22 23 - **Python 3.10+**: Check version with `python --version` 24 - **uv**: Install from [https://github.com/astral-sh/uv](https://github.com/astral-sh/uv) 25 - **Docker**: For local development and testing 26 - **Git**: Version control 27 - **IDE**: VS Code, PyCharm, or Cursor (recommended for AI assistance) 28 29 ### Initial Setup 30 31 1. **Clone and Navigate** 32 ```bash 33 git clone https://github.com/alibaba/OpenSandbox.git 34 cd OpenSandbox/server 35 ``` 36 37 2. **Install Dependencies** 38 ```bash 39 uv sync 40 ``` 41 42 3. **Verify Installation** 43 ```bash 44 uv run python -c "import fastapi; print(fastapi.__version__)" 45 ``` 46 47 4. **Configure Development Environment** 48 ```bash 49 cp opensandbox_server/examples/example.config.toml ~/.sandbox.toml 50 ``` 51 52 Edit `~/.sandbox.toml` for local development: 53 ```toml 54 [server] 55 host = "0.0.0.0" 56 port = 8080 57 api_key = "your-secret-api-key-change-this" 58 59 [log] 60 level = "DEBUG" 61 62 [runtime] 63 type = "docker" 64 execd_image = "opensandbox/execd:v1.0.13" 65 66 [docker] 67 network_mode = "host" 68 ``` 69 70 5. **Run Development Server** 71 ```bash 72 uv run python -m opensandbox_server.main 73 ``` 74 75 ### IDE Configuration 76 77 #### VS Code / Cursor 78 79 Create `.vscode/launch.json`: 80 81 ```json 82 { 83 "version": "0.2.0", 84 "configurations": [ 85 { 86 "name": "Python: FastAPI", 87 "type": "python", 88 "request": "launch", 89 "module": "opensandbox_server.main", 90 "justMyCode": false, 91 "env": { 92 "SANDBOX_CONFIG_PATH": "${workspaceFolder}/.sandbox.toml" 93 } 94 } 95 ] 96 } 97 ``` 98 99 #### PyCharm 100 101 1. Open project in PyCharm 102 2. Configure Python interpreter: **Settings → Project → Python Interpreter** 103 3. Select the virtual environment created by `uv sync` 104 4. Enable pytest: **Settings → Tools → Python Integrated Tools → Testing → pytest** 105 106 ## Project Structure 107 108 ``` 109 server/ 110 ├── opensandbox_server/ # Source code 111 │ ├── main.py # FastAPI application entry point 112 │ ├── config.py # Configuration management 113 │ ├── api/ # API layer 114 │ │ ├── lifecycle.py # Sandbox lifecycle routes 115 │ │ └── schema.py # Pydantic models 116 │ ├── middleware/ # Middleware components 117 │ │ └── auth.py # API Key authentication 118 │ └── services/ # Business logic layer 119 │ ├── sandbox_service.py # Abstract base class 120 │ ├── docker.py # Docker implementation 121 │ └── factory.py # Service factory 122 ├── tests/ # Test suite 123 ├── scripts/ # Utility scripts 124 ├── pyproject.toml # Project metadata and dependencies 125 └── example.config.toml # Example configuration 126 ``` 127 128 ## Architecture Deep Dive 129 130 ### Layered Architecture 131 132 The server follows a clean layered architecture: 133 134 1. **HTTP Layer** (FastAPI routes) - Request validation and response serialization 135 2. **Middleware Layer** - Authentication and cross-cutting concerns 136 3. **Service Layer** - Business logic abstraction 137 4. **Runtime Implementation Layer** - Docker/Kubernetes specific code 138 139 ### Request Flow 140 141 #### Create Sandbox (Async) 142 143 ``` 144 Client → POST /sandboxes 145 ↓ 146 Auth Middleware validates API key 147 ↓ 148 lifecycle.create_sandbox() receives CreateSandboxRequest 149 ↓ 150 sandbox_service.create_sandbox_async(request) 151 ↓ 152 Returns 202 Accepted with Pending status immediately 153 ↓ 154 Background thread provisions the sandbox 155 ``` 156 157 ### Internal Systems 158 159 #### Expiration Timer System 160 161 Tracks sandbox timeouts using in-memory data structures: 162 - `_sandbox_expirations: Dict[str, datetime]` - Expiration times 163 - `_expiration_timers: Dict[str, Timer]` - Active timer threads 164 - `_expiration_lock: Lock` - Thread synchronization 165 166 #### Async Provisioning System 167 168 Avoids blocking API requests during slow operations by: 169 1. Storing sandboxes in pending state 170 2. Starting background provisioning thread 171 3. Returning 202 Accepted immediately 172 4. Transitioning to running state when ready 173 174 ## Development Workflow 175 176 ### Feature Development 177 178 ```bash 179 git checkout -b feature/my-feature 180 # Implement feature 181 uv run pytest 182 git commit -m "feat: add my feature" 183 git push origin feature/my-feature 184 ``` 185 186 ### Bug Fixes 187 188 ```bash 189 git checkout -b fix/bug-description 190 # Write failing test 191 # Fix bug 192 uv run pytest 193 git commit -m "fix: resolve bug" 194 ``` 195 196 ## Testing Guide 197 198 ### Running Tests 199 > **Note**: A local Docker daemon is required to run the full test suite, as integration tests interact with the Docker Engine. 200 201 ```bash 202 # All tests 203 uv run pytest 204 205 # Specific file 206 uv run pytest tests/test_docker_service.py 207 208 # With coverage 209 uv run pytest --cov=opensandbox_server --cov-report=html 210 ``` 211 212 ### Writing Tests 213 214 Example unit test: 215 216 ```python 217 @patch("opensandbox_server.services.docker.docker") 218 def test_create_sandbox_validates_entrypoint(mock_docker): 219 service = DockerSandboxService(config=test_config()) 220 request = CreateSandboxRequest( 221 image=ImageSpec(uri="python:3.11"), 222 timeout=120, 223 entrypoint=[] # Invalid 224 ) 225 with pytest.raises(HTTPException): 226 service.create_sandbox(request) 227 ``` 228 229 ## Working with Docker Runtime 230 231 ### Local Development 232 233 ```bash 234 # Use local Docker 235 export DOCKER_HOST="unix:///var/run/docker.sock" 236 uv run python -m opensandbox_server.main 237 238 # Use remote Docker 239 export DOCKER_HOST="ssh://user@remote-host" 240 uv run python -m opensandbox_server.main 241 ``` 242 243 ### Network Modes 244 245 **Host Mode (Default):** 246 - Sandboxes share host network 247 - Direct port access 248 - Endpoint format: `http://{domain}/{sandbox_id}/{port}` 249 250 **Bridge Mode:** 251 - Isolated networks 252 - HTTP proxy required 253 - Endpoint format: `http://{server}/route/{sandbox_id}/{port}/path` 254 255 ### Egress sidecar (bridge + `networkPolicy`) 256 257 - Config: set `[egress].image`; sidecar starts only when the request carries `networkPolicy`. Requires Docker `network_mode="bridge"`. 258 - Network & privileges: main container shares the sidecar netns (`network_mode=container:<sidecar>`); main container explicitly drops `NET_ADMIN`; sidecar keeps `NET_ADMIN` to manage iptables / DNS transparent redirect. 259 - Ports: host port bindings live on the sidecar; main container labels record the mapped ports for upstream endpoint resolution. 260 - Lifecycle: on create failure / delete / expiration / abnormal recovery, the sidecar is cleaned up; startup also removes orphaned sidecars. 261 - Injection: `OPENSANDBOX_EGRESS_RULES` env passes the `networkPolicy` JSON; sidecar image is pulled/ensured before start. 262 263 ## Working with Kubernetes Runtime 264 265 > **Status:** Planned / Configuration Ready 266 267 Architecture will include: 268 - Pod management with execd init container 269 - Service/Ingress for networking 270 - CronJob or operator for expiration handling 271 272 ## Code Style and Standards 273 274 Follow PEP 8 with Ruff enforcement: 275 276 ```bash 277 uv run ruff check opensandbox_server tests 278 ``` 279 280 ### Naming Conventions 281 282 - Functions: `snake_case` 283 - Classes: `PascalCase` 284 - Constants: `UPPER_SNAKE_CASE` 285 - Private: `_leading_underscore` 286 287 ### Type Hints 288 289 Always use type hints: 290 291 ```python 292 def get_sandbox(self, sandbox_id: str) -> Sandbox: 293 pass 294 ``` 295 296 ## Debugging 297 298 ### Enable Debug Logging 299 300 ```toml 301 [log] 302 level = "DEBUG" 303 ``` 304 305 ### Interactive Debugging 306 307 Use VS Code/Cursor breakpoints or: 308 309 ```python 310 breakpoint() # Python 3.7+ 311 ``` 312 313 ### Docker Debugging 314 315 ```python 316 import logging 317 logging.getLogger("docker").setLevel(logging.DEBUG) 318 ``` 319 320 ## Performance Optimization 321 322 ### Profiling 323 324 ```bash 325 python -m cProfile -o profile.stats -m opensandbox_server.main 326 ``` 327 328 ### Optimization Tips 329 330 1. **Async Operations**: Use async provisioning to avoid blocking 331 2. **Connection Pooling**: Reuse Docker client connections 332 3. **Caching**: Cache configuration and frequently accessed data 333 4. **Resource Limits**: Set appropriate container resource limits 334 5. **Monitoring**: Track container creation/deletion metrics 335 336 ## Contributing 337 338 ### Pull Request Process 339 340 1. Fork the repository 341 2. Create feature branch from `main` 342 3. Write tests for new functionality 343 4. Ensure all tests pass: `uv run pytest` 344 5. Run linter: `uv run ruff check` 345 6. Write clear commit messages 346 7. Submit PR with description 347 348 ### Code Review Guidelines 349 350 - Focus on readability and maintainability 351 - Ensure test coverage for new code 352 - Check for proper error handling 353 - Verify documentation updates 354 - Test Docker and potential Kubernetes compatibility 355 356 ### Commit Message Format 357 358 ``` 359 <type>: <description> 360 361 Types: feat, fix, docs, style, refactor, test, chore 362 ``` 363 364 Examples: 365 - `feat: add Kubernetes runtime support` 366 - `fix: resolve expiration timer memory leak` 367 - `docs: update API documentation` 368 369 --- 370 371 For questions or support, please open an issue on the project repository.