Skip to content

Commit 6e48a93

Browse files
authored
Merge pull request #61 from miciav/feature/runner-controller-ui
Feature/runner controller UI
2 parents b2b5ed8 + e270168 commit 6e48a93

218 files changed

Lines changed: 16663 additions & 6178 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build and Development Commands
6+
7+
```bash
8+
# Setup
9+
uv venv && uv pip install -e ".[dev]"
10+
11+
# Run tests
12+
uv run pytest tests/ # all tests
13+
uv run pytest tests/unit/ # unit tests only
14+
uv run pytest -m "unit" # by marker
15+
uv run pytest -m "not e2e and not multipass" # exclude slow tests
16+
uv run pytest tests/unit/lb_runner/test_foo.py::test_bar # single test
17+
18+
# Quick smoke test
19+
uv run python example.py
20+
21+
# Linting & formatting
22+
uv run black .
23+
uv run flake8
24+
uv run mypy lb_runner lb_controller lb_app lb_ui
25+
26+
# Docs
27+
uv pip install -e ".[docs,controller]"
28+
uv run mkdocs serve
29+
```
30+
31+
## Architecture
32+
33+
The library follows a layered architecture with strict import boundaries:
34+
35+
```
36+
lb_ui/ → lb_app/ → lb_controller/ → lb_runner/
37+
(CLI/TUI) (Stable facade) (Orchestration) (Execution)
38+
39+
lb_plugins/
40+
(Workloads)
41+
```
42+
43+
**Module responsibilities:**
44+
- `lb_runner/` - Core execution: metric collectors (PSUtil, CLI, perf, eBPF), local runner
45+
- `lb_controller/` - Remote orchestration via Ansible, run journaling, state machine
46+
- `lb_app/` - Stable API facade for CLI/UI integrations
47+
- `lb_ui/` - CLI/TUI implementation (does not import into runner/controller)
48+
- `lb_analytics/` - Data aggregation and reporting (Pandas, Matplotlib)
49+
- `lb_plugins/` - Workload plugins (stress-ng, fio, dd, hpl, stream, dfaas)
50+
- `lb_provisioner/` - Docker/Multipass provisioning helpers
51+
- `lb_common/` - Shared utilities and logging configuration
52+
53+
**Key rules:**
54+
- Always use the public `api.py` exports: `lb_runner.api`, `lb_controller.api`, `lb_app.api`
55+
- Never import internal modules directly (enforced by flake8-tidy-imports in `.flake8`)
56+
- Configure logging via `lb_common.api.configure_logging()` in entrypoints
57+
- Keep stdout clean for `LB_EVENT` streaming when building custom UIs
58+
59+
## Plugin System
60+
61+
Workloads are registered via Python entry points in `pyproject.toml`:
62+
63+
```toml
64+
[project.entry-points."linux_benchmark.workloads"]
65+
stress_ng = "lb_plugins.plugins.stress_ng.plugin:PLUGIN"
66+
```
67+
68+
Each plugin in `lb_plugins/plugins/<name>/` contains:
69+
- `plugin.py` - Plugin definition and `PLUGIN` constant
70+
- `generator.py` - Command generation logic
71+
- `ansible/` - Optional Ansible playbooks for setup/teardown
72+
73+
## Test Organization
74+
75+
Tests live in `tests/` with markers for filtering:
76+
- `tests/unit/` - Fast, isolated tests (subdirs: `lb_runner/`, `lb_controller/`, etc.)
77+
- `tests/integration/` - Service-level tests, no provisioning
78+
- `tests/e2e/` - Full end-to-end with Multipass VMs or Docker
79+
- `tests/fixtures/` - Static test data
80+
81+
Markers: `unit`, `integration`, `e2e`, `docker`, `multipass`, `slow`, `slowest`
82+
83+
## Output Directories
84+
85+
Generated at runtime (gitignored):
86+
- `benchmark_results/` - Raw metric data per run
87+
- `reports/` - Generated text reports and plots
88+
- `data_exports/` - Exported data files
89+
90+
## Style
91+
92+
- Python 3.12+, Black (88 chars), strict MyPy
93+
- `snake_case` for functions/variables, `PascalCase` for classes
94+
- Prefer dataclasses for configuration objects
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Delete legacy_materials folder in dfaas plugin
2+
3+
## Problem
4+
5+
The folder `lb_plugins/plugins/dfaas/legacy_materials/` contains legacy Python code that:
6+
7+
1. **Has extreme complexity** - `samples-generator.py:main` has rank **E** (highest complexity)
8+
2. **Has dependency issues** - imports `utils` module that isn't declared, uses `requests` as transitive dependency
9+
3. **Is not used** - no imports from this folder found anywhere in the codebase
10+
4. **Pollutes analysis** - triggers deptry, xenon, and bandit warnings
11+
12+
## Evidence
13+
14+
**Xenon complexity report** (`arch_report/xenon.txt`):
15+
```
16+
ERROR:xenon:block "lb_plugins/plugins/dfaas/legacy_materials/samples_generator/samples-generator.py:46 main" has a rank of E
17+
ERROR:xenon:module 'lb_plugins/plugins/dfaas/legacy_materials/samples_generator/samples-generator.py' has a rank of E
18+
```
19+
20+
**Deptry dependency issues** (`arch_report/deptry.txt`):
21+
```
22+
lb_plugins/plugins/dfaas/legacy_materials/samples_generator/samples-generator-profiler.py:13:1: DEP001 'utils' imported but missing
23+
lb_plugins/plugins/dfaas/legacy_materials/samples_generator/samples-generator.py:16:8: DEP001 'utils' imported but missing
24+
lb_plugins/plugins/dfaas/legacy_materials/samples_generator/utils.py:20:8: DEP003 'requests' imported but transitive
25+
```
26+
27+
**Files to delete**:
28+
```
29+
lb_plugins/plugins/dfaas/legacy_materials/
30+
├── README.md
31+
├── data_collection_doc.md
32+
├── infrastructure/
33+
├── minikube_builder.sh
34+
├── requirements.txt
35+
└── samples_generator/
36+
├── samples-generator.py (rank E)
37+
├── samples-generator-profiler.py (rank C)
38+
└── utils.py (rank C)
39+
```
40+
41+
## Solution
42+
43+
### Step 1: Verify no imports exist
44+
```bash
45+
grep -r "legacy_materials" --include="*.py" .
46+
grep -r "samples_generator" --include="*.py" . | grep -v legacy_materials
47+
```
48+
49+
### Step 2: Delete the folder
50+
```bash
51+
rm -rf lb_plugins/plugins/dfaas/legacy_materials/
52+
```
53+
54+
### Step 3: Run tests
55+
```bash
56+
uv run pytest tests/unit/lb_plugins/test_dfaas*.py -v
57+
```
58+
59+
### Step 4: Verify clean analysis
60+
```bash
61+
uv run deptry lb_plugins
62+
```
63+
64+
## Risk Assessment
65+
66+
| Aspect | Level | Notes |
67+
|--------|-------|-------|
68+
| Risk | **Low** | No imports found, code is orphaned |
69+
| Effort | **Trivial** | Single `rm -rf` command |
70+
| Validation | Run unit tests for dfaas plugin |
71+
72+
## Acceptance Criteria
73+
74+
- [ ] `legacy_materials/` folder deleted
75+
- [ ] No grep matches for `legacy_materials` in codebase
76+
- [ ] `deptry` reports 0 issues for lb_plugins
77+
- [ ] All dfaas tests pass
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Fix import cycle in dfaas plugin
2+
3+
## Problem
4+
5+
There is a circular import dependency in the dfaas plugin:
6+
7+
```
8+
lb_plugins.plugins.dfaas.generator
9+
→ lb_plugins.plugins.dfaas.plugin
10+
→ lb_plugins.plugins.dfaas.generator
11+
```
12+
13+
This cycle:
14+
1. **Breaks modularity** - generator and plugin are tightly coupled
15+
2. **Causes import errors** - can fail at runtime depending on import order
16+
3. **Prevents testing in isolation** - can't test generator without plugin
17+
18+
## Evidence
19+
20+
**Grimp cycles report** (`arch_report/grimp_cycles.txt`):
21+
```
22+
Import cycles in lb_plugins: 1
23+
- lb_plugins.plugins.dfaas.generator -> lb_plugins.plugins.dfaas.plugin -> lb_plugins.plugins.dfaas.generator
24+
```
25+
26+
## Root Cause Analysis
27+
28+
Looking at the imports:
29+
30+
**generator.py** imports from plugin.py:
31+
- Likely imports `DfaasPlugin` or plugin-level constants
32+
33+
**plugin.py** imports from generator.py:
34+
- Imports `DfaasGenerator` to instantiate in `create_generator()`
35+
36+
## Solution
37+
38+
### Option A: Extract shared types to a new module (Recommended)
39+
40+
Create `lb_plugins/plugins/dfaas/types.py` for shared types:
41+
42+
```python
43+
# lb_plugins/plugins/dfaas/types.py
44+
"""Shared types and constants for dfaas plugin."""
45+
46+
from dataclasses import dataclass
47+
from typing import List, Dict, Any
48+
49+
@dataclass
50+
class DfaasResult:
51+
"""Result from a dfaas benchmark run."""
52+
success: bool
53+
metrics: Dict[str, Any]
54+
k6_output: str
55+
# ... other shared types
56+
```
57+
58+
Then update imports:
59+
- `generator.py`: imports from `types.py` instead of `plugin.py`
60+
- `plugin.py`: imports from `types.py` and `generator.py`
61+
62+
### Option B: Use TYPE_CHECKING guard
63+
64+
```python
65+
# generator.py
66+
from typing import TYPE_CHECKING
67+
68+
if TYPE_CHECKING:
69+
from .plugin import DfaasPlugin # Only for type hints
70+
```
71+
72+
### Option C: Lazy import in generator
73+
74+
```python
75+
# generator.py
76+
def some_method(self):
77+
from .plugin import something # Import inside method
78+
```
79+
80+
## Implementation Steps
81+
82+
### Step 1: Identify what generator imports from plugin
83+
```bash
84+
grep -n "from.*plugin import\|import.*plugin" lb_plugins/plugins/dfaas/generator.py
85+
```
86+
87+
### Step 2: Create types.py with shared types
88+
```python
89+
# lb_plugins/plugins/dfaas/types.py
90+
# Move shared types here
91+
```
92+
93+
### Step 3: Update generator.py imports
94+
```python
95+
# Before
96+
from .plugin import SomeType
97+
98+
# After
99+
from .types import SomeType
100+
```
101+
102+
### Step 4: Verify cycle is broken
103+
```bash
104+
uv run grimp lb_plugins --show-cycles
105+
```
106+
107+
### Step 5: Run tests
108+
```bash
109+
uv run pytest tests/unit/lb_plugins/test_dfaas*.py -v
110+
```
111+
112+
## Risk Assessment
113+
114+
| Aspect | Level | Notes |
115+
|--------|-------|-------|
116+
| Risk | **Medium** | Changing import structure |
117+
| Effort | **Low** | ~1 hour |
118+
| Validation | Grimp check + unit tests |
119+
120+
## Acceptance Criteria
121+
122+
- [ ] `grimp` reports 0 cycles in lb_plugins
123+
- [ ] `lb_plugins/plugins/dfaas/types.py` created (if using Option A)
124+
- [ ] All dfaas tests pass
125+
- [ ] No runtime import errors

0 commit comments

Comments
 (0)