Skip to content

Commit 0a5416a

Browse files
Alexander Martynenkoclaude
andcommitted
fix: resolve mypy errors and fix .dockerignore for test builds
mypy (52 errors → 0): - Add int() casts for struct.unpack return values (no-any-return) - Add type annotations to collector.py and app.py route handlers - Fix IPv4/IPv6 union types in FlowKey.from_bytes - Add LbStats bytes property alias to avoid shadowing builtin - Cast int flags to VipFlags/RealFlags enums at call sites - Add assert guards for Optional map types in service.py - Fix BpfMap.__bool__ None check before comparison - Add types-PyYAML to dev dependencies .dockerignore: - Remove tests/ and katran-bpfs/ exclusions that broke integration/e2e Docker builds (COPY tests/, COPY katran-bpfs/) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eed032c commit 0a5416a

15 files changed

Lines changed: 65 additions & 55 deletions

.dockerignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
.venv/
22
.git/
3-
tests/
4-
katran-bpfs/
53
samples/
64
htmlcov/
75
.pytest_cache/

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dev = [
4242
"httpx>=0.24.0",
4343
"ruff>=0.1.0",
4444
"mypy>=1.5.0",
45+
"types-PyYAML>=6.0",
4546
"black>=23.0.0",
4647
]
4748

src/katran/api/rest/app.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _vip_to_response(vip: Any) -> VipResponse:
101101
)
102102

103103

104-
def _lookup_vip(svc, address: str, port: int, protocol: Protocol):
104+
def _lookup_vip(svc: Any, address: str, port: int, protocol: Protocol) -> Any:
105105
"""Get a VIP or raise 404."""
106106
vip = svc.vip_manager.get_vip(address=address, port=port, protocol=protocol)
107107
if vip is None:
@@ -117,7 +117,7 @@ def _lookup_vip(svc, address: str, port: int, protocol: Protocol):
117117
# ---------------------------------------------------------------------------
118118

119119

120-
def get_service(request: Request):
120+
def get_service(request: Request) -> Any:
121121
"""FastAPI dependency that returns the KatranService or raises 503."""
122122
service = request.app.state.service
123123
if service is None or not service.is_running:
@@ -136,7 +136,7 @@ def get_service(request: Request):
136136
}
137137

138138

139-
def _katran_error_handler(request: Request, exc: KatranError) -> JSONResponse:
139+
def _katran_error_handler(request: Request, exc: Exception) -> JSONResponse:
140140
for err_cls, status in _KATRAN_ERROR_STATUS.items():
141141
if isinstance(exc, err_cls):
142142
return JSONResponse(status_code=status, content={"detail": str(exc)})
@@ -148,7 +148,7 @@ def _katran_error_handler(request: Request, exc: KatranError) -> JSONResponse:
148148
# ---------------------------------------------------------------------------
149149

150150

151-
def create_app(service=None) -> FastAPI:
151+
def create_app(service: Any = None) -> FastAPI:
152152
"""Create the FastAPI application."""
153153
app = FastAPI(title="Katran Control Plane", version="0.1.0")
154154
app.state.service = service
@@ -163,7 +163,7 @@ def create_app(service=None) -> FastAPI:
163163
registry = CollectorRegistry()
164164
registry.register(KatranMetricsCollector(service))
165165

166-
def _metrics_handler():
166+
def _metrics_handler() -> Response:
167167
try:
168168
output = generate_latest(registry)
169169
return Response(content=output, media_type=CONTENT_TYPE_LATEST)
@@ -184,7 +184,7 @@ def _metrics_handler():
184184
# --- Health -----------------------------------------------------------
185185

186186
@app.get("/health")
187-
def health():
187+
def health() -> dict[str, str]:
188188
svc = app.state.service
189189
if svc is None or not svc.is_running:
190190
raise HTTPException(status_code=503, detail="Service not running")
@@ -193,7 +193,7 @@ def health():
193193
# --- VIP endpoints ----------------------------------------------------
194194

195195
@app.post("/api/v1/vips", status_code=201, response_model=VipResponse)
196-
def add_vip(req: AddVipRequest, svc=Depends(get_service)):
196+
def add_vip(req: AddVipRequest, svc: Any = Depends(get_service)) -> VipResponse:
197197
proto = _parse_protocol(req.protocol)
198198
vip = svc.vip_manager.add_vip(
199199
address=req.address,
@@ -209,8 +209,8 @@ def get_vips(
209209
address: Optional[str] = Query(None),
210210
port: Optional[int] = Query(None),
211211
protocol: Optional[str] = Query(None),
212-
svc=Depends(get_service),
213-
):
212+
svc: Any = Depends(get_service),
213+
) -> Any:
214214
params = [address, port, protocol]
215215
provided = sum(p is not None for p in params)
216216

@@ -224,8 +224,8 @@ def get_vips(
224224
detail="Provide all of address, port, protocol — or none",
225225
)
226226

227-
# Single VIP lookup
228-
proto = _parse_protocol(protocol)
227+
# Single VIP lookup — all three are non-None at this point
228+
proto = _parse_protocol(protocol or "")
229229
vip = svc.vip_manager.get_vip(address=address, port=port, protocol=proto)
230230
if vip is None:
231231
raise HTTPException(
@@ -235,7 +235,7 @@ def get_vips(
235235
return _vip_to_response(vip)
236236

237237
@app.post("/api/v1/vips/remove")
238-
def remove_vip(req: VipId, svc=Depends(get_service)):
238+
def remove_vip(req: VipId, svc: Any = Depends(get_service)) -> dict[str, str]:
239239
proto = _parse_protocol(req.protocol)
240240
removed = svc.vip_manager.remove_vip(
241241
address=req.address,
@@ -253,7 +253,7 @@ def remove_vip(req: VipId, svc=Depends(get_service)):
253253
# --- Backend endpoints ------------------------------------------------
254254

255255
@app.post("/api/v1/backends/add", status_code=201, response_model=RealResponse)
256-
def add_backend(req: AddBackendRequest, svc=Depends(get_service)):
256+
def add_backend(req: AddBackendRequest, svc: Any = Depends(get_service)) -> RealResponse:
257257
proto = _parse_protocol(req.vip.protocol)
258258
vip = _lookup_vip(svc, req.vip.address, req.vip.port, proto)
259259
real = svc.real_manager.add_real(vip, address=req.address, weight=req.weight)
@@ -267,7 +267,7 @@ def add_backend(req: AddBackendRequest, svc=Depends(get_service)):
267267
return RealResponse(address=str(real.address), weight=real.weight, index=real.index)
268268

269269
@app.post("/api/v1/backends/remove")
270-
def remove_backend(req: BackendId, svc=Depends(get_service)):
270+
def remove_backend(req: BackendId, svc: Any = Depends(get_service)) -> dict[str, str]:
271271
proto = _parse_protocol(req.vip.protocol)
272272
vip = _lookup_vip(svc, req.vip.address, req.vip.port, proto)
273273
removed = svc.real_manager.remove_real(vip, address=req.address)
@@ -283,7 +283,7 @@ def remove_backend(req: BackendId, svc=Depends(get_service)):
283283
return {"status": "removed"}
284284

285285
@app.post("/api/v1/backends/drain")
286-
def drain_backend(req: BackendId, svc=Depends(get_service)):
286+
def drain_backend(req: BackendId, svc: Any = Depends(get_service)) -> dict[str, str]:
287287
proto = _parse_protocol(req.vip.protocol)
288288
vip = _lookup_vip(svc, req.vip.address, req.vip.port, proto)
289289
drained = svc.real_manager.drain_real(vip, address=req.address)

src/katran/bpf/map_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def _bpf_syscall(cmd: int, attr: ctypes.Structure, size: int) -> int:
128128
if result < 0:
129129
err = ctypes.get_errno()
130130
return -err
131-
return result
131+
return int(result)
132132

133133

134134
# =============================================================================
@@ -500,7 +500,7 @@ def values(self) -> Iterator[V]:
500500

501501
def __bool__(self) -> bool:
502502
"""Map is truthy when the FD is open."""
503-
return self._fd >= 0
503+
return self._fd is not None and self._fd >= 0
504504

505505
def __contains__(self, key: K) -> bool:
506506
"""Check if key exists in map."""

src/katran/bpf/maps/ch_rings_map.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@ def _serialize_key(self, key: int) -> bytes:
8585
return struct.pack("<I", key)
8686

8787
def _deserialize_key(self, data: bytes) -> int:
88-
return struct.unpack("<I", data)[0]
88+
return int(struct.unpack("<I", data)[0])
8989

9090
def _serialize_value(self, value: int) -> bytes:
9191
return struct.pack("<I", value)
9292

9393
def _deserialize_value(self, data: bytes) -> int:
94-
return struct.unpack("<I", data)[0]
94+
return int(struct.unpack("<I", data)[0])
9595

9696
# -------------------------------------------------------------------------
9797
# Ring index calculation

src/katran/bpf/maps/ctl_array.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def _serialize_key(self, key: int) -> bytes:
6767
return struct.pack("<I", key)
6868

6969
def _deserialize_key(self, data: bytes) -> int:
70-
return struct.unpack("<I", data)[0]
70+
return int(struct.unpack("<I", data)[0])
7171

7272
def _serialize_value(self, value: CtlValue) -> bytes:
7373
return value.to_bytes()

src/katran/bpf/maps/hc_reals_map.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ def _serialize_key(self, key: int) -> bytes:
8686
return struct.pack("<I", key)
8787

8888
def _deserialize_key(self, data: bytes) -> int:
89-
return struct.unpack("<I", data)[0]
89+
return int(struct.unpack("<I", data)[0])
9090

9191
def _serialize_value(self, value: int) -> bytes:
9292
return struct.pack("<I", value)
9393

9494
def _deserialize_value(self, data: bytes) -> int:
95-
return struct.unpack("<I", data)[0]
95+
return int(struct.unpack("<I", data)[0])
9696

9797
# -------------------------------------------------------------------------
9898
# Healthcheck mapping operations

src/katran/bpf/maps/lru_map.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
from __future__ import annotations
2727

28+
from typing import Any
29+
2830
from katran.bpf.map_manager import BpfMap
2931
from katran.core.constants import DEFAULT_LRU_SIZE
3032
from katran.core.types import (
@@ -178,7 +180,7 @@ def invalidate_backend(self, real_index: int) -> int:
178180

179181
return count
180182

181-
def get_cache_stats(self) -> dict[str, int]:
183+
def get_cache_stats(self) -> dict[str, Any]:
182184
"""
183185
Get LRU cache statistics.
184186

src/katran/bpf/maps/reals_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def _serialize_key(self, key: int) -> bytes:
8181
return struct.pack("<I", key)
8282

8383
def _deserialize_key(self, data: bytes) -> int:
84-
return struct.unpack("<I", data)[0]
84+
return int(struct.unpack("<I", data)[0])
8585

8686
def _serialize_value(self, value: RealDefinition) -> bytes:
8787
return value.to_bytes()

src/katran/bpf/maps/stats_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def _serialize_key(self, key: int) -> bytes:
111111
return struct.pack("<I", key)
112112

113113
def _deserialize_key(self, data: bytes) -> int:
114-
return struct.unpack("<I", data)[0]
114+
return int(struct.unpack("<I", data)[0])
115115

116116
def _serialize_value(self, value: LbStats) -> bytes:
117117
return value.to_bytes()

0 commit comments

Comments
 (0)