namespaces favouites

This commit is contained in:
vy.boyko 2025-10-25 21:11:59 +03:00
parent b76941ec3a
commit 3cf37a3389
3 changed files with 239 additions and 6 deletions

View File

@ -5,11 +5,13 @@
## Особенности ## Особенности
- 🎯 Интерактивное меню с навигацией стрелками - 🎯 Интерактивное меню с навигацией стрелками
- ⭐ Избранные namespaces для быстрого доступа
- 🎨 Красивый вывод с использованием Rich - 🎨 Красивый вывод с использованием Rich
- 📦 Управление namespaces, deployments, pods, ConfigMaps - 📦 Управление namespaces, deployments, pods, ConfigMaps
- 🔄 Быстрый рестарт и масштабирование deployments - 🔄 Быстрый рестарт и масштабирование deployments
- 📝 Просмотр логов pods - 📝 Просмотр логов pods
- 🔍 Просмотр содержимого ConfigMaps - 🔍 Просмотр содержимого ConfigMaps
- 💾 Сохранение настроек в `~/.config/k8s-tool/k8s-tool.cfg`
## Требования ## Требования
@ -58,6 +60,18 @@ poetry run k8s-tool
#### Выбор Namespace #### Выбор Namespace
Выберите namespace для работы. Все последующие операции будут выполняться в выбранном namespace. Выберите namespace для работы. Все последующие операции будут выполняться в выбранном namespace.
Избранные namespaces отображаются в начале списка с пометкой ⭐ для быстрого доступа. Список избранных сохраняется между запусками приложения.
#### Управление избранными
Добавляйте часто используемые namespaces в избранное для быстрого доступа:
- Добавить/удалить текущий namespace в/из избранного
- Просмотр всех избранных namespaces
- Добавление любого namespace в избранное из полного списка
- Удаление namespace из избранного
- Очистка всех избранных
Конфигурация сохраняется в файле `~/.config/k8s-tool/k8s-tool.cfg`.
#### Список Deployments #### Список Deployments
Отображает все deployments в текущем namespace с информацией о: Отображает все deployments в текущем namespace с информацией о:
- Имени deployment - Имени deployment
@ -92,9 +106,10 @@ poetry run k8s-tool
## Архитектура ## Архитектура
Проект состоит из двух основных модулей: Проект состоит из трёх основных модулей:
- `k8s_client.py` - обёртка над Kubernetes Python API для работы с кластером - `k8s_client.py` - обёртка над Kubernetes Python API для работы с кластером
- `config.py` - менеджер конфигурации для управления избранными namespaces
- `main.py` - главное приложение с интерактивным меню - `main.py` - главное приложение с интерактивным меню
### Технологии ### Технологии

87
k8s_tool/config.py Normal file
View File

@ -0,0 +1,87 @@
"""Configuration manager for k8s-tool."""
import json
import os
from pathlib import Path
from typing import List, Dict, Any
from rich.console import Console
console = Console()
class ConfigManager:
"""Manage application configuration."""
def __init__(self):
"""Initialize configuration manager."""
self.config_dir = Path.home() / ".config" / "k8s-tool"
self.config_file = self.config_dir / "k8s-tool.cfg"
self._ensure_config_dir()
self.config = self._load_config()
def _ensure_config_dir(self):
"""Ensure configuration directory exists."""
try:
self.config_dir.mkdir(parents=True, exist_ok=True)
except Exception as e:
console.print(f"[yellow]Warning:[/yellow] Could not create config directory: {e}")
def _load_config(self) -> Dict[str, Any]:
"""Load configuration from file."""
if not self.config_file.exists():
return {"favorites": []}
try:
with open(self.config_file, 'r') as f:
return json.load(f)
except Exception as e:
console.print(f"[yellow]Warning:[/yellow] Could not load config: {e}")
return {"favorites": []}
def _save_config(self):
"""Save configuration to file."""
try:
with open(self.config_file, 'w') as f:
json.dump(self.config, f, indent=2)
except Exception as e:
console.print(f"[red]Error:[/red] Could not save config: {e}")
def get_favorites(self) -> List[str]:
"""Get list of favorite namespaces."""
return self.config.get("favorites", [])
def add_favorite(self, namespace: str) -> bool:
"""Add namespace to favorites."""
favorites = self.config.get("favorites", [])
if namespace not in favorites:
favorites.append(namespace)
self.config["favorites"] = sorted(favorites)
self._save_config()
console.print(f"[green]✓[/green] Added [cyan]{namespace}[/cyan] to favorites")
return True
else:
console.print(f"[yellow]![/yellow] [cyan]{namespace}[/cyan] is already in favorites")
return False
def remove_favorite(self, namespace: str) -> bool:
"""Remove namespace from favorites."""
favorites = self.config.get("favorites", [])
if namespace in favorites:
favorites.remove(namespace)
self.config["favorites"] = favorites
self._save_config()
console.print(f"[green]✓[/green] Removed [cyan]{namespace}[/cyan] from favorites")
return True
else:
console.print(f"[yellow]![/yellow] [cyan]{namespace}[/cyan] is not in favorites")
return False
def is_favorite(self, namespace: str) -> bool:
"""Check if namespace is in favorites."""
return namespace in self.config.get("favorites", [])
def clear_favorites(self):
"""Clear all favorites."""
self.config["favorites"] = []
self._save_config()
console.print("[green]✓[/green] Cleared all favorites")

View File

@ -9,6 +9,7 @@ from rich.panel import Panel
from rich.syntax import Syntax from rich.syntax import Syntax
from k8s_tool.k8s_client import K8sClient from k8s_tool.k8s_client import K8sClient
from k8s_tool.config import ConfigManager
console = Console() console = Console()
@ -32,6 +33,7 @@ class K8sTool:
def __init__(self): def __init__(self):
"""Initialize the application.""" """Initialize the application."""
self.k8s_client = K8sClient() self.k8s_client = K8sClient()
self.config = ConfigManager()
self.current_namespace: Optional[str] = None self.current_namespace: Optional[str] = None
def run(self): def run(self):
@ -58,10 +60,13 @@ class K8sTool:
def _main_menu(self): def _main_menu(self):
"""Display main menu.""" """Display main menu."""
namespace_info = f"[cyan]{self.current_namespace}[/cyan]" if self.current_namespace else "[dim]not selected[/dim]" namespace_info = f"[cyan]{self.current_namespace}[/cyan]" if self.current_namespace else "[dim]not selected[/dim]"
console.print(f"\n[bold]Current namespace:[/bold] {namespace_info}") is_favorite = self.config.is_favorite(self.current_namespace) if self.current_namespace else False
fav_indicator = "" if is_favorite else ""
console.print(f"\n[bold]Current namespace:[/bold] {namespace_info}{fav_indicator}")
choices = [ choices = [
"Select Namespace", "Select Namespace",
"Manage Favorites",
"List Deployments", "List Deployments",
"Restart Deployment", "Restart Deployment",
"Scale Deployment", "Scale Deployment",
@ -78,6 +83,8 @@ class K8sTool:
if action == "Select Namespace": if action == "Select Namespace":
self._select_namespace() self._select_namespace()
elif action == "Manage Favorites":
self._manage_favorites()
elif action == "List Deployments": elif action == "List Deployments":
self._list_deployments() self._list_deployments()
elif action == "Restart Deployment": elif action == "Restart Deployment":
@ -101,15 +108,139 @@ class K8sTool:
console.print("[red]No namespaces found[/red]") console.print("[red]No namespaces found[/red]")
return return
namespace = questionary.select( # Get favorites
favorites = self.config.get_favorites()
# Separate favorites and non-favorites
fav_namespaces = [ns for ns in namespaces if ns in favorites]
other_namespaces = [ns for ns in namespaces if ns not in favorites]
# Create choices with stars for favorites
choices = []
if fav_namespaces:
choices.extend([f"{ns}" for ns in fav_namespaces])
if other_namespaces:
choices.append(questionary.Separator("" * 40))
choices.extend(other_namespaces)
selected = questionary.select(
"Select namespace:", "Select namespace:",
choices=namespaces, choices=choices,
style=custom_style
).ask()
if selected:
# Remove star prefix if present
namespace = selected.replace("", "")
self.current_namespace = namespace
is_fav = self.config.is_favorite(namespace)
fav_text = " (favorite)" if is_fav else ""
console.print(f"[green]✓[/green] Namespace set to: [cyan]{namespace}[/cyan]{fav_text}")
def _manage_favorites(self):
"""Manage favorite namespaces."""
choices = []
if self.current_namespace:
if self.config.is_favorite(self.current_namespace):
choices.append(f"Remove '{self.current_namespace}' from favorites")
else:
choices.append(f"Add '{self.current_namespace}' to favorites")
choices.extend([
"View all favorites",
"Add namespace to favorites",
"Remove namespace from favorites",
"Clear all favorites",
"Back to main menu"
])
action = questionary.select(
"Favorites management:",
choices=choices,
style=custom_style
).ask()
if not action or action == "Back to main menu":
return
if action.startswith("Add '") and action.endswith("' to favorites"):
# Add current namespace
self.config.add_favorite(self.current_namespace)
elif action.startswith("Remove '") and action.endswith("' from favorites"):
# Remove current namespace
self.config.remove_favorite(self.current_namespace)
elif action == "View all favorites":
self._view_favorites()
elif action == "Add namespace to favorites":
self._add_namespace_to_favorites()
elif action == "Remove namespace from favorites":
self._remove_namespace_from_favorites()
elif action == "Clear all favorites":
if questionary.confirm("Are you sure you want to clear all favorites?", style=custom_style, default=False).ask():
self.config.clear_favorites()
def _view_favorites(self):
"""View all favorite namespaces."""
favorites = self.config.get_favorites()
if not favorites:
console.print("[yellow]No favorites yet[/yellow]")
return
console.print("\n[bold]Favorite namespaces:[/bold]")
for ns in favorites:
indicator = " [dim](current)[/dim]" if ns == self.current_namespace else ""
console.print(f" ⭐ [cyan]{ns}[/cyan]{indicator}")
console.print()
def _add_namespace_to_favorites(self):
"""Add a namespace to favorites."""
console.print("[dim]Fetching namespaces...[/dim]")
namespaces = self.k8s_client.get_namespaces()
if not namespaces:
console.print("[red]No namespaces found[/red]")
return
# Filter out already favorite namespaces
favorites = self.config.get_favorites()
available = [ns for ns in namespaces if ns not in favorites]
if not available:
console.print("[yellow]All namespaces are already in favorites[/yellow]")
return
namespace = questionary.select(
"Select namespace to add to favorites:",
choices=available,
style=custom_style style=custom_style
).ask() ).ask()
if namespace: if namespace:
self.current_namespace = namespace self.config.add_favorite(namespace)
console.print(f"[green]✓[/green] Namespace set to: [cyan]{namespace}[/cyan]")
def _remove_namespace_from_favorites(self):
"""Remove a namespace from favorites."""
favorites = self.config.get_favorites()
if not favorites:
console.print("[yellow]No favorites to remove[/yellow]")
return
namespace = questionary.select(
"Select namespace to remove from favorites:",
choices=favorites,
style=custom_style
).ask()
if namespace:
self.config.remove_favorite(namespace)
def _ensure_namespace_selected(self) -> bool: def _ensure_namespace_selected(self) -> bool:
"""Ensure namespace is selected.""" """Ensure namespace is selected."""