Skip to content

Commit 382256f

Browse files
committed
Feedback from PR review.
1 parent 4829225 commit 382256f

File tree

2 files changed

+36
-37
lines changed

2 files changed

+36
-37
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555

5656
- name: Install Python dependencies
5757
run: |
58-
pip install pytest pytest-timeout requests psycopg2-binary
58+
pip install pytest pytest-timeout requests types-requests psycopg2-binary
5959
6060
- name: Start K3s cluster
6161
uses: jupyterhub/action-k3s-helm@v4

.github/workflows/tests/test_autoscaling.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
import subprocess
66
import threading
77
import time
8+
from typing import Any, Dict, List, Optional, cast
89

910
import pytest
1011
import requests
1112

1213

13-
def get_namespace():
14+
def get_namespace() -> str:
1415
return os.environ.get("NAMESPACE", "eoapi")
1516

1617

17-
def get_release_name():
18+
def get_release_name() -> str:
1819
return os.environ.get("RELEASE_NAME", "eoapi")
1920

2021

21-
def get_base_url():
22+
def get_base_url() -> str:
2223
namespace = get_namespace()
2324

2425
# Check if we have an ingress
@@ -48,7 +49,12 @@ def get_base_url():
4849
return "http://localhost:8080"
4950

5051

51-
def kubectl_get(resource, namespace=None, label_selector=None, output="json"):
52+
def kubectl_get(
53+
resource: str,
54+
namespace: Optional[str] = None,
55+
label_selector: Optional[str] = None,
56+
output: str = "json",
57+
) -> subprocess.CompletedProcess[str]:
5258
cmd = ["kubectl", "get", resource]
5359

5460
if namespace:
@@ -64,7 +70,7 @@ def kubectl_get(resource, namespace=None, label_selector=None, output="json"):
6470
return result
6571

6672

67-
def get_pod_metrics(namespace, service_name):
73+
def get_pod_metrics(namespace: str, service_name: str) -> List[Dict[str, str]]:
6874
release_name = get_release_name()
6975
result = subprocess.run(
7076
[
@@ -84,7 +90,7 @@ def get_pod_metrics(namespace, service_name):
8490
if result.returncode != 0:
8591
return []
8692

87-
metrics = []
93+
metrics: List[Dict[str, str]] = []
8894
for line in result.stdout.strip().split("\n"):
8995
if line.strip():
9096
parts = line.split()
@@ -97,20 +103,21 @@ def get_pod_metrics(namespace, service_name):
97103
return metrics
98104

99105

100-
def get_hpa_status(namespace, hpa_name):
106+
def get_hpa_status(namespace: str, hpa_name: str) -> Optional[Dict[str, Any]]:
107+
"""Get HPA status for a specific HPA."""
101108
result = kubectl_get("hpa", namespace=namespace, output="json")
102109
if result.returncode != 0:
103110
return None
104111

105112
hpas = json.loads(result.stdout)
106113
for hpa in hpas["items"]:
107114
if hpa["metadata"]["name"] == hpa_name:
108-
return hpa
115+
return cast(Dict[str, Any], hpa)
109116

110117
return None
111118

112119

113-
def get_pod_count(namespace, service_name):
120+
def get_pod_count(namespace: str, service_name: str) -> int:
114121
release_name = get_release_name()
115122
result = kubectl_get(
116123
"pods",
@@ -129,7 +136,7 @@ def get_pod_count(namespace, service_name):
129136
return len(running_pods)
130137

131138

132-
def make_request(url, timeout=10):
139+
def make_request(url: str, timeout: int = 10) -> bool:
133140
"""Make a single HTTP request and return success status."""
134141
try:
135142
response = requests.get(url, timeout=timeout)
@@ -139,14 +146,18 @@ def make_request(url, timeout=10):
139146

140147

141148
def generate_load(
142-
base_url, endpoints, duration=60, concurrent_requests=5, delay=0.1
143-
):
149+
base_url: str,
150+
endpoints: List[str],
151+
duration: int = 60,
152+
concurrent_requests: int = 5,
153+
delay: float = 0.1,
154+
) -> Dict[str, Any]:
144155
"""Generate HTTP load against specified endpoints."""
145156
end_time = time.time() + duration
146157
success_count = 0
147158
error_count = 0
148159

149-
def worker():
160+
def worker() -> None:
150161
nonlocal success_count, error_count
151162
while time.time() < end_time:
152163
for endpoint in endpoints:
@@ -179,10 +190,7 @@ def worker():
179190

180191

181192
class TestHPAConfiguration:
182-
"""Test HPA resource configuration and basic functionality."""
183-
184-
def test_hpa_resources_properly_configured(self):
185-
"""Verify HPA resources have correct configuration."""
193+
def test_hpa_resources_properly_configured(self) -> None:
186194
namespace = get_namespace()
187195
result = kubectl_get("hpa", namespace=namespace)
188196

@@ -196,7 +204,6 @@ def test_hpa_resources_properly_configured(self):
196204
spec = hpa["spec"]
197205
hpa_name = hpa["metadata"]["name"]
198206

199-
# Check required fields
200207
assert "scaleTargetRef" in spec, (
201208
f"HPA {hpa_name} missing scaleTargetRef"
202209
)
@@ -206,19 +213,16 @@ def test_hpa_resources_properly_configured(self):
206213
f"HPA {hpa_name} missing metrics configuration"
207214
)
208215

209-
# Validate replica bounds
210216
min_replicas = spec["minReplicas"]
211217
max_replicas = spec["maxReplicas"]
212218
assert min_replicas > 0, f"HPA {hpa_name} minReplicas must be > 0"
213219
assert max_replicas > min_replicas, (
214220
f"HPA {hpa_name} maxReplicas must be > minReplicas"
215221
)
216222

217-
# Check metrics configuration
218223
metrics = spec["metrics"]
219224
assert len(metrics) > 0, f"HPA {hpa_name} has no metrics configured"
220225

221-
# Verify at least one metric is CPU
222226
cpu_metrics = [
223227
m
224228
for m in metrics
@@ -233,8 +237,7 @@ def test_hpa_resources_properly_configured(self):
233237
f"✅ HPA {hpa_name}: {min_replicas}-{max_replicas} replicas, {len(metrics)} metrics"
234238
)
235239

236-
def test_target_deployments_exist(self):
237-
"""Verify HPA target deployments exist and are ready."""
240+
def test_target_deployments_exist(self) -> None:
238241
namespace = get_namespace()
239242
result = kubectl_get("hpa", namespace=namespace)
240243

@@ -281,9 +284,7 @@ def test_target_deployments_exist(self):
281284

282285

283286
class TestCPUScaling:
284-
"""Test CPU-based autoscaling functionality."""
285-
286-
def test_cpu_metrics_collection(self):
287+
def test_cpu_metrics_collection(self) -> None:
287288
"""Verify CPU metrics are being collected for HPA targets."""
288289
namespace = get_namespace()
289290
services = ["stac", "raster", "vector"]
@@ -306,7 +307,7 @@ def test_cpu_metrics_collection(self):
306307
"No CPU metrics available for any service"
307308
)
308309

309-
def test_hpa_cpu_utilization_calculation(self):
310+
def test_hpa_cpu_utilization_calculation(self) -> None:
310311
"""Verify HPA calculates CPU utilization correctly."""
311312
namespace = get_namespace()
312313
result = kubectl_get("hpa", namespace=namespace)
@@ -359,7 +360,7 @@ def test_hpa_cpu_utilization_calculation(self):
359360
else:
360361
print(f"⚠️ HPA {hpa_name} no CPU metrics available yet")
361362

362-
def test_cpu_resource_requests_alignment(self):
363+
def test_cpu_resource_requests_alignment(self) -> None:
363364
"""Verify CPU resource requests are properly set for percentage calculations."""
364365
namespace = get_namespace()
365366
services = ["stac", "raster", "vector"]
@@ -419,7 +420,7 @@ class TestScalingBehavior:
419420
"""Test actual scaling behavior under load."""
420421

421422
@pytest.mark.slow
422-
def test_load_response_scaling(self):
423+
def test_load_response_scaling(self) -> None:
423424
"""Generate load and verify scaling response (when possible)."""
424425
namespace = get_namespace()
425426
base_url = get_base_url()
@@ -433,7 +434,7 @@ def test_load_response_scaling(self):
433434
]
434435

435436
# Check initial state
436-
initial_pod_counts = {}
437+
initial_pod_counts: Dict[str, int] = {}
437438
services = ["stac", "raster", "vector"]
438439

439440
for service in services:
@@ -473,7 +474,7 @@ def test_load_response_scaling(self):
473474
time.sleep(30)
474475

475476
# Check final state
476-
final_pod_counts = {}
477+
final_pod_counts: Dict[str, int] = {}
477478
for service in services:
478479
final_pod_counts[service] = get_pod_count(namespace, service)
479480

@@ -501,7 +502,6 @@ def test_load_response_scaling(self):
501502
)
502503
print(f"Post-load HPA {hpa_name} CPU: {cpu_utilization}%")
503504

504-
# Verify load test was successful
505505
assert load_stats["success_rate"] > 0.8, (
506506
f"Load test had low success rate: {load_stats['success_rate']:.2%}"
507507
)
@@ -524,7 +524,7 @@ def test_load_response_scaling(self):
524524
"⚠️ No scaling occurred - may be due to CI resource constraints or low load thresholds"
525525
)
526526

527-
def test_scaling_stabilization_windows(self):
527+
def test_scaling_stabilization_windows(self) -> None:
528528
"""Verify HPA respects stabilization windows in configuration."""
529529
namespace = get_namespace()
530530
result = kubectl_get("hpa", namespace=namespace)
@@ -538,7 +538,6 @@ def test_scaling_stabilization_windows(self):
538538
hpa_name = hpa["metadata"]["name"]
539539
spec = hpa["spec"]
540540

541-
# Check if behavior is configured
542541
behavior = spec.get("behavior", {})
543542
if not behavior:
544543
print(f"⚠️ HPA {hpa_name} has no scaling behavior configured")
@@ -566,7 +565,7 @@ def test_scaling_stabilization_windows(self):
566565
class TestRequestRateScaling:
567566
"""Test request rate-based autoscaling (when available)."""
568567

569-
def test_custom_metrics_for_request_rate(self):
568+
def test_custom_metrics_for_request_rate(self) -> None:
570569
"""Check if custom metrics for request rate scaling are available."""
571570
namespace = get_namespace()
572571

@@ -600,7 +599,7 @@ def test_custom_metrics_for_request_rate(self):
600599
"⚠️ No request rate metrics available - may require ingress controller metrics configuration"
601600
)
602601

603-
def test_hpa_request_rate_metrics(self):
602+
def test_hpa_request_rate_metrics(self) -> None:
604603
"""Verify HPA can access request rate metrics (when configured)."""
605604
namespace = get_namespace()
606605
result = kubectl_get("hpa", namespace=namespace)

0 commit comments

Comments
 (0)