configmaps editing
This commit is contained in:
parent
75a30aba8b
commit
36bcbb6847
10
README.md
10
README.md
@ -10,7 +10,7 @@
|
|||||||
- 📦 Управление namespaces, deployments, pods, ConfigMaps
|
- 📦 Управление namespaces, deployments, pods, ConfigMaps
|
||||||
- 🔄 Быстрый рестарт и масштабирование deployments
|
- 🔄 Быстрый рестарт и масштабирование deployments
|
||||||
- 📝 Просмотр логов pods
|
- 📝 Просмотр логов pods
|
||||||
- 🔍 Просмотр содержимого ConfigMaps
|
- 🔍 Просмотр и редактирование ConfigMaps в текстовом редакторе
|
||||||
- 💾 Сохранение настроек в `~/.config/k8s-tool/k8s-tool.cfg`
|
- 💾 Сохранение настроек в `~/.config/k8s-tool/k8s-tool.cfg`
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
@ -95,6 +95,14 @@ poetry run k8s-tool
|
|||||||
|
|
||||||
Можно выбрать ConfigMap для просмотра его содержимого с подсветкой синтаксиса.
|
Можно выбрать ConfigMap для просмотра его содержимого с подсветкой синтаксиса.
|
||||||
|
|
||||||
|
#### Редактирование ConfigMap
|
||||||
|
Отдельный пункт меню для редактирования ConfigMaps:
|
||||||
|
- Выберите ConfigMap из списка
|
||||||
|
- Открывается текстовый редактор (из переменной окружения `$EDITOR` или `$VISUAL`, по умолчанию `vi`)
|
||||||
|
- Редактируйте YAML с данными ConfigMap
|
||||||
|
- После сохранения показывается diff изменений (добавленные, удаленные, измененные ключи)
|
||||||
|
- Подтверждение перед применением изменений в кластер
|
||||||
|
|
||||||
#### Просмотр логов Pod
|
#### Просмотр логов Pod
|
||||||
Выберите pod и контейнер (если их несколько) для просмотра логов. Можно указать количество строк для отображения (по умолчанию 100).
|
Выберите pod и контейнер (если их несколько) для просмотра логов. Можно указать количество строк для отображения (по умолчанию 100).
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,21 @@ class K8sClient:
|
|||||||
console.print(f"[red]Error reading ConfigMap:[/red] {e}")
|
console.print(f"[red]Error reading ConfigMap:[/red] {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def update_configmap(self, namespace: str, name: str, data: Dict[str, str]) -> bool:
|
||||||
|
"""Update ConfigMap data."""
|
||||||
|
try:
|
||||||
|
# Read the existing ConfigMap first
|
||||||
|
cm = self.v1.read_namespaced_config_map(name, namespace)
|
||||||
|
# Update only the data field
|
||||||
|
cm.data = data
|
||||||
|
# Patch the ConfigMap
|
||||||
|
self.v1.patch_namespaced_config_map(name, namespace, cm)
|
||||||
|
console.print(f"[green]✓[/green] ConfigMap {name} updated successfully")
|
||||||
|
return True
|
||||||
|
except ApiException as e:
|
||||||
|
console.print(f"[red]Error updating ConfigMap:[/red] {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def restart_deployment(self, namespace: str, name: str) -> bool:
|
def restart_deployment(self, namespace: str, name: str) -> bool:
|
||||||
"""Restart deployment by updating annotation."""
|
"""Restart deployment by updating annotation."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
111
k8s_tool/main.py
111
k8s_tool/main.py
@ -1,7 +1,11 @@
|
|||||||
"""Main application entry point."""
|
"""Main application entry point."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import yaml
|
||||||
|
from typing import Optional, Dict
|
||||||
import questionary
|
import questionary
|
||||||
from questionary import Style
|
from questionary import Style
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
@ -71,6 +75,7 @@ class K8sTool:
|
|||||||
"Restart Deployment",
|
"Restart Deployment",
|
||||||
"Scale Deployment",
|
"Scale Deployment",
|
||||||
"View ConfigMaps",
|
"View ConfigMaps",
|
||||||
|
"Edit ConfigMap",
|
||||||
"View Pod Logs",
|
"View Pod Logs",
|
||||||
"Exit"
|
"Exit"
|
||||||
]
|
]
|
||||||
@ -93,6 +98,8 @@ class K8sTool:
|
|||||||
self._scale_deployment()
|
self._scale_deployment()
|
||||||
elif action == "View ConfigMaps":
|
elif action == "View ConfigMaps":
|
||||||
self._view_configmaps()
|
self._view_configmaps()
|
||||||
|
elif action == "Edit ConfigMap":
|
||||||
|
self._edit_configmaps()
|
||||||
elif action == "View Pod Logs":
|
elif action == "View Pod Logs":
|
||||||
self._view_pod_logs()
|
self._view_pod_logs()
|
||||||
elif action == "Exit":
|
elif action == "Exit":
|
||||||
@ -386,6 +393,108 @@ class K8sTool:
|
|||||||
console.print(Syntax(value, "yaml", line_numbers=False, background_color="default"))
|
console.print(Syntax(value, "yaml", line_numbers=False, background_color="default"))
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
def _edit_configmaps(self):
|
||||||
|
"""Edit a ConfigMap."""
|
||||||
|
if not self._ensure_namespace_selected():
|
||||||
|
return
|
||||||
|
|
||||||
|
console.print(f"[dim]Fetching ConfigMaps in {self.current_namespace}...[/dim]")
|
||||||
|
configmaps = self.k8s_client.get_configmaps(self.current_namespace)
|
||||||
|
|
||||||
|
if not configmaps:
|
||||||
|
console.print("[yellow]No ConfigMaps found[/yellow]")
|
||||||
|
return
|
||||||
|
|
||||||
|
cm_names = [cm['name'] for cm in configmaps]
|
||||||
|
cm_name = questionary.select(
|
||||||
|
"Select ConfigMap to edit:",
|
||||||
|
choices=cm_names,
|
||||||
|
style=custom_style
|
||||||
|
).ask()
|
||||||
|
|
||||||
|
if cm_name:
|
||||||
|
data = self.k8s_client.get_configmap_data(self.current_namespace, cm_name)
|
||||||
|
if data:
|
||||||
|
self._edit_configmap(cm_name, data)
|
||||||
|
|
||||||
|
def _edit_configmap(self, cm_name: str, current_data: Dict[str, str]):
|
||||||
|
"""Edit ConfigMap data in text editor."""
|
||||||
|
# Get editor from environment or use default
|
||||||
|
editor = os.environ.get('EDITOR', os.environ.get('VISUAL', 'vi'))
|
||||||
|
|
||||||
|
# Create temporary file with ConfigMap data
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as tf:
|
||||||
|
temp_file = tf.name
|
||||||
|
# Write current data as YAML
|
||||||
|
yaml.dump(current_data, tf, default_flow_style=False, allow_unicode=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get modification time before editing
|
||||||
|
mtime_before = os.path.getmtime(temp_file)
|
||||||
|
|
||||||
|
# Open editor
|
||||||
|
subprocess.call([editor, temp_file])
|
||||||
|
|
||||||
|
# Check if file was modified
|
||||||
|
mtime_after = os.path.getmtime(temp_file)
|
||||||
|
|
||||||
|
if mtime_after == mtime_before:
|
||||||
|
console.print("[yellow]No changes made[/yellow]")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read edited data
|
||||||
|
with open(temp_file, 'r') as f:
|
||||||
|
try:
|
||||||
|
new_data = yaml.safe_load(f)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
console.print(f"[red]Error parsing YAML:[/red] {e}")
|
||||||
|
if questionary.confirm("Retry editing?", style=custom_style, default=False).ask():
|
||||||
|
self._edit_configmap(cm_name, current_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Validate that new_data is a dict
|
||||||
|
if not isinstance(new_data, dict):
|
||||||
|
console.print("[red]Error:[/red] ConfigMap data must be a dictionary")
|
||||||
|
if questionary.confirm("Retry editing?", style=custom_style, default=False).ask():
|
||||||
|
self._edit_configmap(cm_name, current_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convert all values to strings (ConfigMap requirement)
|
||||||
|
new_data = {k: str(v) if not isinstance(v, str) else v for k, v in new_data.items()}
|
||||||
|
|
||||||
|
# Show diff
|
||||||
|
console.print("\n[bold]Changes:[/bold]")
|
||||||
|
removed_keys = set(current_data.keys()) - set(new_data.keys())
|
||||||
|
added_keys = set(new_data.keys()) - set(current_data.keys())
|
||||||
|
modified_keys = {k for k in current_data.keys() & new_data.keys()
|
||||||
|
if current_data[k] != new_data[k]}
|
||||||
|
|
||||||
|
if removed_keys:
|
||||||
|
console.print(f"[red]Removed keys:[/red] {', '.join(removed_keys)}")
|
||||||
|
if added_keys:
|
||||||
|
console.print(f"[green]Added keys:[/green] {', '.join(added_keys)}")
|
||||||
|
if modified_keys:
|
||||||
|
console.print(f"[yellow]Modified keys:[/yellow] {', '.join(modified_keys)}")
|
||||||
|
|
||||||
|
if not (removed_keys or added_keys or modified_keys):
|
||||||
|
console.print("[yellow]No changes detected[/yellow]")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Confirm update
|
||||||
|
if questionary.confirm(
|
||||||
|
f"Apply changes to ConfigMap '{cm_name}'?",
|
||||||
|
style=custom_style,
|
||||||
|
default=False
|
||||||
|
).ask():
|
||||||
|
self.k8s_client.update_configmap(self.current_namespace, cm_name, new_data)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Clean up temp file
|
||||||
|
try:
|
||||||
|
os.unlink(temp_file)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _view_pod_logs(self):
|
def _view_pod_logs(self):
|
||||||
"""View pod logs."""
|
"""View pod logs."""
|
||||||
if not self._ensure_namespace_selected():
|
if not self._ensure_namespace_selected():
|
||||||
|
|||||||
@ -10,7 +10,8 @@ requires-python = "^3.9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"questionary (>=2.1.1,<3.0.0)",
|
"questionary (>=2.1.1,<3.0.0)",
|
||||||
"kubernetes (>=34.1.0,<35.0.0)",
|
"kubernetes (>=34.1.0,<35.0.0)",
|
||||||
"rich (>=14.2.0,<15.0.0)"
|
"rich (>=14.2.0,<15.0.0)",
|
||||||
|
"pyyaml (>=6.0.3,<7.0.0)"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user