first commit
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Интерактивное или пакетное удаление кластера kind и локальной папки с конфигами.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
|
||||
Пакетный режим: ``--non-interactive --name ИМЯ [--yes]`` (``--yes`` пропускает подтверждение).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from core.cluster_lifecycle import KindClusterError, delete_kind_cluster_and_data
|
||||
from kind_k8s_paths import clusters_dir
|
||||
|
||||
|
||||
def _configure_logging() -> None:
|
||||
if logging.root.handlers:
|
||||
return
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
|
||||
|
||||
|
||||
def _run(cmd: list[str]) -> int:
|
||||
p = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if p.stdout:
|
||||
print(p.stdout, end="")
|
||||
if p.stderr:
|
||||
print(p.stderr, end="", file=sys.stderr)
|
||||
return p.returncode
|
||||
|
||||
|
||||
def _list_kind_clusters() -> list[str]:
|
||||
p = subprocess.run(["kind", "get", "clusters"], capture_output=True, text=True)
|
||||
if p.returncode != 0:
|
||||
return []
|
||||
lines = [x.strip() for x in (p.stdout or "").splitlines() if x.strip()]
|
||||
return [x for x in lines if x.lower() not in ("no kind clusters found.", "no kind clusters found")]
|
||||
|
||||
|
||||
def _ask(prompt: str) -> str:
|
||||
return input(f"{prompt}: ").strip()
|
||||
|
||||
|
||||
def _interactive() -> None:
|
||||
print("=== Удаление кластера kind ===\n")
|
||||
CLUSTERS_DIR = clusters_dir()
|
||||
if not shutil.which("kind"):
|
||||
print("Не найден kind.", file=sys.stderr)
|
||||
print(" Через Docker: make -C kind-k8s-develop delete (или make delete из каталога репозитория).", file=sys.stderr)
|
||||
sys.exit(127)
|
||||
|
||||
clusters = _list_kind_clusters()
|
||||
if not clusters:
|
||||
print("Нет зарегистрированных кластеров kind.")
|
||||
only_dirs = (
|
||||
sorted(p.name for p in CLUSTERS_DIR.iterdir() if p.is_dir() and not p.name.startswith("."))
|
||||
if CLUSTERS_DIR.is_dir()
|
||||
else []
|
||||
)
|
||||
if only_dirs:
|
||||
print("Есть локальные папки в clusters/: ", ", ".join(only_dirs))
|
||||
name = _ask("Удалить только данные в clusters/<имя> (без kind delete)? Введите имя или пусто для выхода")
|
||||
if not name:
|
||||
return
|
||||
d = CLUSTERS_DIR / name
|
||||
if d.is_dir():
|
||||
shutil.rmtree(d)
|
||||
print(f"Удалена папка {d}")
|
||||
else:
|
||||
print("Папка не найдена.")
|
||||
return
|
||||
|
||||
print("Существующие кластеры kind:")
|
||||
for i, c in enumerate(clusters, 1):
|
||||
print(f" {i}) {c}")
|
||||
raw = _ask("\nВведите имя кластера или номер из списка")
|
||||
if not raw:
|
||||
print("Отмена.")
|
||||
return
|
||||
if raw.isdigit():
|
||||
idx = int(raw, 10)
|
||||
if idx < 1 or idx > len(clusters):
|
||||
print("Неверный номер.")
|
||||
sys.exit(1)
|
||||
name = clusters[idx - 1]
|
||||
else:
|
||||
name = raw
|
||||
if name not in clusters:
|
||||
print(f"Кластер «{name}» не найден в kind. Отмена.")
|
||||
sys.exit(1)
|
||||
|
||||
confirm = _ask(f"Удалить кластер «{name}» и папку clusters/{name}? (yes/no)")
|
||||
if confirm.lower() not in ("yes", "y", "да", "д"):
|
||||
print("Отмена.")
|
||||
return
|
||||
|
||||
try:
|
||||
kind_ok, summary = delete_kind_cluster_and_data(name=name, log_to_stdout=True)
|
||||
except KindClusterError as e:
|
||||
print(str(e), file=sys.stderr)
|
||||
raise SystemExit(getattr(e, "exit_code", 1)) from e
|
||||
|
||||
if not kind_ok:
|
||||
print("kind delete завершился с ошибкой; локальная папка удалена при наличии.", file=sys.stderr)
|
||||
print(summary)
|
||||
print("Готово.")
|
||||
|
||||
|
||||
def _parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="Удаление кластера kind")
|
||||
p.add_argument("--non-interactive", action="store_true", help="Без диалогов")
|
||||
p.add_argument("--name", help="Имя кластера")
|
||||
p.add_argument("--yes", "-y", action="store_true", help="Не спрашивать подтверждение (только с --non-interactive)")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_configure_logging()
|
||||
args = _parse_args()
|
||||
|
||||
if args.non_interactive:
|
||||
if not args.name:
|
||||
print("Нужен --name в режиме --non-interactive.", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
if not args.yes:
|
||||
print("Добавьте --yes для подтверждения удаления в пакетном режиме.", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
if not shutil.which("kind"):
|
||||
print("Не найден kind.", file=sys.stderr)
|
||||
sys.exit(127)
|
||||
try:
|
||||
kind_ok, summary = delete_kind_cluster_and_data(name=args.name.strip())
|
||||
except KindClusterError as e:
|
||||
print(str(e), file=sys.stderr)
|
||||
raise SystemExit(getattr(e, "exit_code", 1)) from e
|
||||
print(summary)
|
||||
raise SystemExit(0 if kind_ok else 1)
|
||||
|
||||
_interactive()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user