test: tags print
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
import docker
|
||||||
|
from ctypes.wintypes import tagSIZE
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
running_containers = []
|
||||||
|
sleeping_containers = []
|
||||||
|
for c in client.containers.list(all=True):
|
||||||
|
info = c.attrs # словарь со всеми данными
|
||||||
|
image = info["Config"]["Image"] # образ
|
||||||
|
created = info["Created"] # время создания
|
||||||
|
labels = info["Config"].get("Labels", {}) # лейблы
|
||||||
|
ports = info["NetworkSettings"]["Ports"] # проброшенные порты ЪЪЪ
|
||||||
|
mounts = info.get("Mounts", []) # маунты
|
||||||
|
status = c.status # статус контейнера (вкл-выкл)
|
||||||
|
tags = c.image.tags
|
||||||
|
container = {
|
||||||
|
"name": c.name,
|
||||||
|
"image": image,
|
||||||
|
"id": c.id[:12],
|
||||||
|
"create_time": created,
|
||||||
|
"mounted_data": mounts,
|
||||||
|
"labels": labels
|
||||||
|
}
|
||||||
|
if status == "running":
|
||||||
|
running_containers.append(container)
|
||||||
|
else:
|
||||||
|
sleeping_containers.append(container)
|
||||||
|
print(tags)
|
||||||
-162
@@ -1,162 +0,0 @@
|
|||||||
import docker
|
|
||||||
import re
|
|
||||||
from typing import List, Tuple, Optional, Dict
|
|
||||||
|
|
||||||
SAFE_VERSIONS: Dict[str, str] = { #Потом тут будет взятие версий и CVE из базы CVE
|
|
||||||
"nginx": "1.25.4",
|
|
||||||
"redis": "7.2.4",
|
|
||||||
"postgres": "16.2",
|
|
||||||
"mysql": "8.0.36",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def parse_image_ref(image_ref: str) -> Tuple[str, str]:
|
|
||||||
if ":" in image_ref:
|
|
||||||
repo, tag = image_ref.rsplit(":", 1)
|
|
||||||
else:
|
|
||||||
repo, tag = image_ref, "latest"
|
|
||||||
name = repo.split("/")[-1]
|
|
||||||
return name, tag
|
|
||||||
|
|
||||||
|
|
||||||
def extract_version_from_tag(tag: str) -> Optional[str]:
|
|
||||||
tag = tag.strip()
|
|
||||||
if tag in ("latest", "", "<none>"):
|
|
||||||
return None
|
|
||||||
base = re.split(r"[^0-9.]", tag)[0]
|
|
||||||
if not base or not re.search(r"\d", base):
|
|
||||||
return None
|
|
||||||
return base
|
|
||||||
|
|
||||||
|
|
||||||
def version_tuple(v: str) -> Tuple[int, ...]:
|
|
||||||
parts = v.split(".")
|
|
||||||
nums: List[int] = []
|
|
||||||
for p in parts:
|
|
||||||
if p.isdigit():
|
|
||||||
nums.append(int(p))
|
|
||||||
else:
|
|
||||||
# На всякий случай вытащим ведущие цифры
|
|
||||||
m = re.match(r"(\d+)", p)
|
|
||||||
if m:
|
|
||||||
nums.append(int(m.group(1)))
|
|
||||||
return tuple(nums)
|
|
||||||
|
|
||||||
|
|
||||||
def compare_versions(v1: str, v2: str) -> int:
|
|
||||||
|
|
||||||
t1 = version_tuple(v1)
|
|
||||||
t2 = version_tuple(v2)
|
|
||||||
max_len = max(len(t1), len(t2))
|
|
||||||
t1 += (0,) * (max_len - len(t1))
|
|
||||||
t2 += (0,) * (max_len - len(t2))
|
|
||||||
if t1 < t2:
|
|
||||||
return -1
|
|
||||||
if t1 > t2:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def detect_service(name: str) -> Optional[str]:
|
|
||||||
lname = name.lower()
|
|
||||||
for service in SAFE_VERSIONS.keys():
|
|
||||||
if service in lname:
|
|
||||||
return service
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
client = docker.from_env()
|
|
||||||
containers = client.containers.list(all=True)
|
|
||||||
|
|
||||||
rows = []
|
|
||||||
for c in containers:
|
|
||||||
image_tags = c.image.tags
|
|
||||||
if image_tags:
|
|
||||||
image_ref = image_tags[0]
|
|
||||||
else:
|
|
||||||
image_ref = c.attrs["Config"]["Image"]
|
|
||||||
|
|
||||||
service_name_raw, tag = parse_image_ref(image_ref)
|
|
||||||
service = detect_service(service_name_raw)
|
|
||||||
version = extract_version_from_tag(tag)
|
|
||||||
|
|
||||||
status = "UNKNOWN"
|
|
||||||
note = ""
|
|
||||||
|
|
||||||
if service is None:
|
|
||||||
status = "SKIP"
|
|
||||||
note = "Неизвестный сервис (не в списке SAFE_VERSIONS)"
|
|
||||||
elif version is None:
|
|
||||||
status = "UNKNOWN"
|
|
||||||
note = "Не удалось определить версию из тега"
|
|
||||||
else:
|
|
||||||
safe_ver = SAFE_VERSIONS.get(service)
|
|
||||||
if safe_ver is None:
|
|
||||||
status = "UNKNOWN"
|
|
||||||
note = "Нет эталонной версии для сравнения"
|
|
||||||
else:
|
|
||||||
cmp_res = compare_versions(version, safe_ver)
|
|
||||||
if cmp_res < 0:
|
|
||||||
status = "OUTDATED"
|
|
||||||
note = f"Версия ниже безопасной ({safe_ver})"
|
|
||||||
elif cmp_res == 0:
|
|
||||||
status = "SAFE"
|
|
||||||
note = "Соответствует безопасной версии"
|
|
||||||
else:
|
|
||||||
status = "NEWER"
|
|
||||||
note = f"Версия выше эталонной ({safe_ver})"
|
|
||||||
|
|
||||||
rows.append(
|
|
||||||
{
|
|
||||||
"container": c.name,
|
|
||||||
"image": image_ref,
|
|
||||||
"service": service or "-",
|
|
||||||
"version": version or "-",
|
|
||||||
"status": status,
|
|
||||||
"note": note,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
headers = ["CONTAINER", "IMAGE", "SERVICE", "VERSION", "STATUS", "NOTE"]
|
|
||||||
col_widths = {h: len(h) for h in headers}
|
|
||||||
|
|
||||||
for r in rows:
|
|
||||||
col_widths["CONTAINER"] = max(col_widths["CONTAINER"], len(r["container"]))
|
|
||||||
col_widths["IMAGE"] = max(col_widths["IMAGE"], len(r["image"]))
|
|
||||||
col_widths["SERVICE"] = max(col_widths["SERVICE"], len(str(r["service"])))
|
|
||||||
col_widths["VERSION"] = max(col_widths["VERSION"], len(str(r["version"])))
|
|
||||||
col_widths["STATUS"] = max(col_widths["STATUS"], len(r["status"]))
|
|
||||||
col_widths["NOTE"] = max(col_widths["NOTE"], len(r["note"]))
|
|
||||||
|
|
||||||
header_line = " ".join(h.ljust(col_widths[h]) for h in headers)
|
|
||||||
sep_line = " ".join("-" * col_widths[h] for h in headers)
|
|
||||||
print(header_line)
|
|
||||||
print(sep_line)
|
|
||||||
|
|
||||||
for r in rows:
|
|
||||||
line = " ".join(
|
|
||||||
[
|
|
||||||
r["container"].ljust(col_widths["CONTAINER"]),
|
|
||||||
r["image"].ljust(col_widths["IMAGE"]),
|
|
||||||
str(r["service"]).ljust(col_widths["SERVICE"]),
|
|
||||||
str(r["version"]).ljust(col_widths["VERSION"]),
|
|
||||||
r["status"].ljust(col_widths["STATUS"]),
|
|
||||||
r["note"].ljust(col_widths["NOTE"]),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
print(line)
|
|
||||||
|
|
||||||
total = len(rows)
|
|
||||||
outdated = sum(1 for r in rows if r["status"] == "OUTDATED")
|
|
||||||
safe = sum(1 for r in rows if r["status"] == "SAFE")
|
|
||||||
unknown = sum(1 for r in rows if r["status"] == "UNKNOWN")
|
|
||||||
print()
|
|
||||||
print(f"Всего контейнеров: {total}")
|
|
||||||
print(f"Безопасных: {safe}")
|
|
||||||
print(f"Устаревших: {outdated}")
|
|
||||||
print(f"Неопределённых: {unknown}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user