-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstate.py
More file actions
72 lines (56 loc) · 2.09 KB
/
state.py
File metadata and controls
72 lines (56 loc) · 2.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import threading
from dataclasses import replace
from src.models import ContainerInfo
class ContainerStateManager:
"""Thread-safe container state manager.
This class is accessed from both the Docker event monitoring thread
and the async event loop, so all access to _containers must be protected.
"""
def __init__(self) -> None:
self._containers: dict[str, ContainerInfo] = {}
self._lock = threading.Lock()
def update(self, info: ContainerInfo) -> None:
with self._lock:
self._containers[info.name] = info
def get(self, name: str) -> ContainerInfo | None:
with self._lock:
c = self._containers.get(name)
return replace(c) if c is not None else None
def get_all(self) -> list[ContainerInfo]:
with self._lock:
return [replace(c) for c in self._containers.values()]
def get_all_names(self) -> set[str]:
with self._lock:
return set(self._containers.keys())
def remove(self, name: str) -> None:
with self._lock:
self._containers.pop(name, None)
def find_by_name(self, partial: str) -> list[ContainerInfo]:
partial_lower = partial.lower()
with self._lock:
# Check for exact match first
for c in self._containers.values():
if c.name.lower() == partial_lower:
return [replace(c)]
# Fall back to substring match
return [
replace(c) for c in self._containers.values()
if partial_lower in c.name.lower()
]
def get_summary(self) -> dict[str, int]:
running = 0
stopped = 0
unhealthy = 0
with self._lock:
for c in self._containers.values():
if c.status == "running":
running += 1
else:
stopped += 1
if c.health == "unhealthy":
unhealthy += 1
return {
"running": running,
"stopped": stopped,
"unhealthy": unhealthy,
}