Skip to content

Commit 37d0549

Browse files
authored
Merge pull request #19 from SQuent/fix-autoliniting--precommit
Fix autoliniting precommit
2 parents aaa18b3 + e9dadd7 commit 37d0549

File tree

15 files changed

+439
-90
lines changed

15 files changed

+439
-90
lines changed

.releaserc.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,18 @@ plugins:
1010
- replacements:
1111
- files:
1212
- "pyproject.toml"
13-
from: "version = \".*\""
14-
to: "version = \"${nextRelease.version}\""
13+
from: "version = '.*'"
14+
to: "version = '${nextRelease.version}'"
15+
results:
16+
- file: "pyproject.toml"
17+
hasChanged: true
18+
numMatches: 1
19+
numReplacements: 1
20+
countMatches: true
21+
- files:
22+
- "pyproject.toml"
23+
from: "version_variable = \\[\\s*\"pyproject.toml:version\"\\s*\\]"
24+
to: "version_variable = [\"pyproject.toml:version\"]"
1525
results:
1626
- file: "pyproject.toml"
1727
hasChanged: true

README.md

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,165 @@
1-
docker build -t stressed-pod . && docker run -p 5000:5000 stressed-pod
1+
# Stressed Pod
2+
3+
## Overview
4+
Stressed Pod is a tool designed to simulate various types of loads on a Kubernetes pod. It enables testing application resilience by generating CPU, memory loads, and producing custom logs. This tool is particularly useful for performance testing, resource sizing, and scaling policy validation.
5+
6+
## Features
7+
- **CPU Load Management**: Precise control of CPU usage (0 to N cores)
8+
- **Memory Load Management**: Memory consumption simulation (in MB)
9+
- **Dynamic Load**: Progressive variation of CPU/memory load
10+
- **Log Generation**: Custom log production with different levels and formats
11+
- **Kubernetes Probes**: Control of readiness/liveness probes
12+
- **REST API**: HTTP interface for load control
13+
- **Environment Variable Configuration**: Flexible behavior parameterization
14+
15+
## Installation
16+
17+
### Requirements
18+
- Python 3.13+
19+
- Poetry for dependency management
20+
21+
### Configuration
22+
23+
#### Environment Variables
24+
```yaml
25+
# CPU Load
26+
ENABLE_DYNAMIC_CPU_LOAD: "false"
27+
INITIAL_CPU_LOAD: "0"
28+
FINAL_CPU_LOAD: "0.5"
29+
CPU_LOAD_DURATION: "60"
30+
STOP_CPU_LOAD_AT_END: "true"
31+
32+
# Memory Load
33+
ENABLE_DYNAMIC_MEMORY_LOAD: "false"
34+
INITIAL_MEMORY_LOAD: "0"
35+
FINAL_MEMORY_LOAD: "256"
36+
MEMORY_LOAD_DURATION: "60"
37+
STOP_MEMORY_LOAD_AT_END: "true"
38+
39+
# Log Configuration
40+
ENABLE_AUTOMATIC_LOGS: "false"
41+
LOG_MESSAGE: "Automatic log message"
42+
LOG_LEVEL: "info"
43+
LOG_SERVICE: "auto-logger"
44+
LOG_FORMAT: "json"
45+
LOG_INTERVAL: "5"
46+
LOG_DURATION: "60"
47+
48+
# Initial Probe States
49+
READINESS_STATUS: "SUCCESS"
50+
LIVENESS_STATUS: "SUCCESS"
51+
52+
# System Configuration
53+
ENABLE_AUTO_TERMINATION: "false"
54+
AUTO_TERMINATION_DELAY: "300"
55+
```
56+
57+
## API Endpoints
58+
59+
### Load Management
60+
- `GET /load`: Current load status
61+
- `POST /load/cpu/start`: Start CPU load
62+
- `POST /load/cpu/stop`: Stop CPU load
63+
- `POST /load/cpu/dynamic`: Configure dynamic CPU load
64+
- `POST /load/memory/start`: Start memory load
65+
- `POST /load/memory/stop`: Stop memory load
66+
- `POST /load/memory/dynamic`: Configure dynamic memory load
67+
68+
### Log Management
69+
- `POST /log`: Create custom logs
70+
71+
### Probe Management
72+
- `GET /probes`: Probe status
73+
- `GET /probes/readiness`: Readiness probe status
74+
- `GET /probes/liveness`: Liveness probe status
75+
- `POST /probes/status`: Modify probe status
76+
77+
### System Management
78+
- `GET /system`: System information
79+
- `POST /system/terminate`: Schedule pod termination
80+
81+
## Usage Examples
82+
83+
### Dynamic CPU Load
84+
```bash
85+
curl -X POST http://localhost:8000/load/cpu/dynamic \
86+
-H "Content-Type: application/json" \
87+
-d '{
88+
"start_value": 0.1,
89+
"end_value": 0.8,
90+
"duration": 60,
91+
"stop_at_end": true
92+
}'
93+
```
94+
95+
### Memory Load
96+
```bash
97+
curl -X POST http://localhost:8000/load/memory/start \
98+
-H "Content-Type: application/json" \
99+
-d '{
100+
"value": 256
101+
}'
102+
```
103+
104+
### Log Generation
105+
```bash
106+
curl -X POST http://localhost:8000/log \
107+
-H "Content-Type: application/json" \
108+
-d '{
109+
"message": "Test message",
110+
"level": "info",
111+
"service": "test-service",
112+
"format": "json",
113+
"interval": 5,
114+
"duration": 60
115+
}'
116+
```
117+
118+
### Probe Control
119+
```bash
120+
curl -X POST http://localhost:8000/probes/status \
121+
-H "Content-Type: application/json" \
122+
-d '{
123+
"probe": "readiness",
124+
"status": "error"
125+
}'
126+
```
127+
128+
## Development
129+
130+
### Launch app
131+
docker-compose up --build
132+
133+
### Running Tests
134+
```bash
135+
# Install dependencies
136+
poetry install
137+
138+
# Run tests
139+
poetry run pytest
140+
141+
# Run tests with coverage
142+
poetry run pytest --cov=app
143+
```
144+
145+
### Code Quality
146+
The project uses:
147+
- pytest for testing
148+
- black for code formatting
149+
- flake8 for linting
150+
- mypy for type checking
151+
152+
## Contributing
153+
Contributions are welcome! Please feel free to submit a Pull Request.
154+
155+
## License
156+
This project is licensed under the MIT License.
157+
158+
## Important Notes
159+
- The CPU load is distributed across all available cores
160+
- Memory load is specified in MB
161+
- Log formats supported: JSON and plaintext
162+
- Probe status changes are immediate
163+
- All durations are in seconds
164+
165+
This tool is designed for testing purposes and should be used with caution in production environments.

app/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
from .routers import load_router, probes_router, system_router, log_router
33

44
app = FastAPI(
5-
title="Load Testing API",
6-
description="API for managing CPU and memory loads, and lifecycle probes",
5+
title="Stressed API",
6+
description="simulates controlled workloads and failures to stress-test a system",
77
version="1.0.0",
88
)
99

app/managers/load_manager.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import signal
33
import subprocess
44
from threading import Timer
5+
import psutil
56

67

78
class LoadManager:
@@ -28,7 +29,10 @@ def __init__(self):
2829
self.cpu_duration = int(os.getenv("CPU_LOAD_DURATION", 60))
2930
self.stop_cpu_at_end = os.getenv("STOP_CPU_LOAD_AT_END", "true") == "true"
3031

31-
# Call dynamic load functions during initialization if enabled
32+
self.max_duration = 3600
33+
self.system_memory = psutil.virtual_memory().total // (1024 * 1024)
34+
self.system_cpus = os.cpu_count()
35+
3236
if os.getenv("ENABLE_DYNAMIC_MEMORY_LOAD", "false") == "true":
3337
self.dynamic_memory_load(
3438
self.memory_at_start,
@@ -62,10 +66,14 @@ def stop_cpu_load(self):
6266
self.cpu_timers.clear()
6367
self.cpu_requested = 0
6468

65-
def add_cpu_load(self, value: int):
66-
"""Add CPU load"""
67-
if value < 0:
69+
def add_cpu_load(self, value: float):
70+
"""Add CPU load with validation"""
71+
if value <= 0:
6872
raise ValueError("CPU load must be greater than 0")
73+
if value > self.system_cpus:
74+
raise ValueError(
75+
f"CPU load cannot exceed system CPU count ({self.system_cpus})"
76+
)
6977

7078
if not os.path.exists(self.cpu_script_path):
7179
raise RuntimeError("CPU stress script is missing")
@@ -93,9 +101,13 @@ def stop_memory_load(self):
93101
raise RuntimeError(f"Failed to terminate memory stress: {e}")
94102

95103
def add_memory_load(self, value: int):
96-
"""Add memory load"""
97-
if value < 0:
104+
"""Add memory load with validation"""
105+
if value <= 0:
98106
raise ValueError("Memory load must be greater than 0")
107+
if value > self.system_memory:
108+
raise ValueError(
109+
f"Memory load cannot exceed system memory ({self.system_memory}MB)"
110+
)
99111

100112
if not os.path.exists(self.memory_script_path):
101113
raise RuntimeError("Memory stress script is missing")
@@ -111,9 +123,13 @@ def add_memory_load(self, value: int):
111123
def dynamic_memory_load(
112124
self, start_value: int, end_value: int, duration: int, stop_at_end: bool = False
113125
):
114-
"""Dynamic memory load"""
115-
if duration < 0:
116-
raise ValueError("Duration must be greater than 0")
126+
"""Dynamic memory load with validation"""
127+
if duration <= 0 or duration > self.max_duration:
128+
raise ValueError(
129+
f"Duration must be between 1 and {self.max_duration} seconds"
130+
)
131+
if end_value < start_value:
132+
raise ValueError("End value must be greater than start value")
117133

118134
try:
119135
start_value = start_value
@@ -149,11 +165,19 @@ def apply_dynamic_memory_load(interval_num):
149165
apply_dynamic_memory_load(0)
150166

151167
def dynamic_cpu_load(
152-
self, start_value: int, end_value: int, duration: int, stop_at_end: bool = False
168+
self,
169+
start_value: float,
170+
end_value: float,
171+
duration: int,
172+
stop_at_end: bool = False,
153173
):
154-
"""Dynamic CPU load"""
155-
if duration < 0:
156-
raise ValueError("Duration must be greater than 0")
174+
"""Dynamic CPU load with validation"""
175+
if duration <= 0 or duration > self.max_duration:
176+
raise ValueError(
177+
f"Duration must be between 1 and {self.max_duration} seconds"
178+
)
179+
if end_value < start_value:
180+
raise ValueError("End value must be greater than start value")
157181

158182
try:
159183
start_value = start_value
@@ -183,7 +207,6 @@ def apply_dynamic_cpu_load(interval_num):
183207
timer.start()
184208

185209
elif stop_at_end:
186-
# Correctly schedule stop_memory_load to execute after the last interval
187210
stop_timer = Timer(10, self.stop_cpu_load)
188211
stop_timer.start()
189212

0 commit comments

Comments
 (0)