Automata-based approach (draft)

This commit is contained in:
Digital Studium 2024-04-09 21:30:05 +03:00
parent 9921116d68
commit 3073d6e9f0
1 changed files with 197 additions and 94 deletions

289
kls
View File

@ -1,11 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import curses import curses
import subprocess import subprocess
from enum import Enum
stdscr = None stdscr = None
def filter_words(words, start):
return list(filter(lambda s: s.startswith(start), words)) class MenuState(Enum):
SELECTED_WITHOUT_SEARCH = 1
SELECTED_WITH_SEARCH = 2
NOT_SELECTED_WITHOUT_SEARCH = 3
NOT_SELECTED_WITH_SEARCH = 4
# я не знаю, что делается в этой функции. # я не знаю, что делается в этой функции.
def init_screen(): def init_screen():
@ -20,7 +26,8 @@ def init_screen():
init_screen() init_screen()
class Menu: class Menu:
def __init__(self, name, rows, begin_x): def __init__(self, name, rows, begin_x, state):
self.state = state
self.name = name # заголовок окна self.name = name # заголовок окна
self.rows = rows # строки окна self.rows = rows # строки окна
self.begin_x = begin_x # где начинается окно по х? self.begin_x = begin_x # где начинается окно по х?
@ -30,6 +37,7 @@ class Menu:
for index, row in enumerate(self.rows): # рисуем строки for index, row in enumerate(self.rows): # рисуем строки
self.win.addstr(index + 3, 2, row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки self.win.addstr(index + 3, 2, row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки
self.row = 0 # выбранная строка self.row = 0 # выбранная строка
self.search_string = ""
# рисуем первое меню # рисуем первое меню
@ -37,27 +45,27 @@ class Menu:
bytes_list = subprocess.check_output("kubectl get ns --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split() bytes_list = subprocess.check_output("kubectl get ns --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
namespaces = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))] namespaces = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
## отрисовываем меню ## отрисовываем меню
menu1 = Menu("Namespaces", namespaces, 0) menu1 = Menu("Namespaces", namespaces, 0, MenuState.SELECTED_WITHOUT_SEARCH)
# рисуем второе меню # рисуем второе меню
## готовим контент ## готовим контент
api_resources = ["pods", "services", "deployments", "ingresses"] api_resources = ["pods", "services", "deployments", "ingresses"]
## отрисовываем меню ## отрисовываем меню
menu2 = Menu("API resources", api_resources, 0 + curses.COLS // 3) menu2 = Menu("API resources", api_resources, 0 + curses.COLS // 3, MenuState.NOT_SELECTED_WITHOUT_SEARCH)
# рисуем третье меню # рисуем третье меню
## готовим контент ## готовим контент
bytes_list = subprocess.check_output("kubectl get pods -n kube-system --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split() bytes_list = subprocess.check_output("kubectl get pods -n kube-system --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
pods = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))] pods = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
## отрисовываем меню ## отрисовываем меню
menu3 = Menu("Resources", pods, 0 + curses.COLS // 3 * 2) menu3 = Menu("Resources", pods, 0 + curses.COLS // 3 * 2, MenuState.NOT_SELECTED_WITHOUT_SEARCH)
menus = [menu1, menu2, menu3] menus = [menu1, menu2, menu3]
def run_command(command, current_menu, rows=None): def run_command(command, current_menu, rows=None):
namespace = menus[0].rows[menus[0].row] namespace = menu1.rows[menu1.row]
api_resource = menus[1].rows[menus[1].row] api_resource = menu2.rows[menu2.row]
resource = menus[2].rows[menus[2].row] resource = menu3.rows[menu3.row]
subprocess.call(eval(command), shell=True) subprocess.call(eval(command), shell=True)
init_screen() init_screen()
for menu in menus: for menu in menus:
@ -102,102 +110,197 @@ def navigate_vertically(direction, current_menu):
run_command(command, current_menu, rows=resources) run_command(command, current_menu, rows=resources)
def filter_menu1():
global menu1
menu1.win.clear()
menu1.win.addstr(curses.LINES - 2, 2, f"/{menu1.search_string}") # рисуем её
menu1.win.clrtoeol() # очищаем остальную часть строки
menu1.win.addstr(1, 2, menu1.name, curses.A_REVERSE | curses.A_ITALIC) # рисуем заголовок
menu1.rows = list(filter(lambda x: (x.startswith(menu1.search_string)), namespaces)) # фильтруем нэймспейсы
for index, row in enumerate(menu1.rows): # рисуем то, что отфильтровали
menu1.win.addstr(index + 3, 2, row)
if menu1.rows:
menu1.win.addstr(3, 2, menu1.rows[menu1.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
menu1.win.box()
menu1.win.refresh()
def main(stdscr): def main(stdscr):
search_mode = False
search_string = "" search_string = ""
stdscr.refresh() stdscr.refresh()
running = True running = True
current_menu = 0 menu1.win.addstr(1, 2, menu1.name, curses.A_REVERSE | curses.A_ITALIC)
menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC)
for menu in menus: for menu in menus:
menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC)
if menu.name == "Namespaces":
menu.win.addstr(curses.LINES - 2, 2, "Press / for search") menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
menu.win.refresh()
while running: while running:
[menu.win.refresh() for menu in menus] # refresh all menus
key_pressed = stdscr.getkey() key_pressed = stdscr.getkey()
match menu1.state:
if current_menu == 0: case MenuState.SELECTED_WITHOUT_SEARCH:
if key_pressed == "/":
search_mode = True
if search_mode:
match key_pressed: match key_pressed:
case '\t' | "KEY_RIGHT": case "/":
current_menu = navigate_horizontally("right", current_menu) menu1.win.addstr(curses.LINES - 2, 2, "/") # рисуем слэш
case "KEY_BTAB" | "KEY_LEFT": menu1.win.clrtoeol() # очищаем остальную часть строки
current_menu = navigate_horizontally("left", current_menu) menu1.win.box() # рисуем рамку
case "KEY_DOWN": menu1.win.refresh()
navigate_vertically("down", current_menu) menu1.state = MenuState.SELECTED_WITH_SEARCH
case "KEY_UP":
navigate_vertically("up", current_menu)
case "KEY_BACKSPACE":
if search_string:
search_string = search_string[:-1]
else:
search_mode = False
if key_pressed.isalpha() or key_pressed == "-":
search_string += key_pressed
elif key_pressed == "/" and search_string == "":
pass
else:
continue continue
init_screen() case "q":
for menu in menus:
menu.win.clear() # очищаем окно
menu.win.box()
menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна
if menu.name == "Namespaces":
menu.rows = list(filter(lambda x: (x.startswith(search_string)), namespaces)) # меняем строки у окна Namespaces
menu.row = 0
if search_mode:
menu.win.addstr(curses.LINES - 2, 2, f"/{search_string}")
else:
menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
elif menu.name == "Resources":
if menus[0].rows:
namespace = menus[0].rows[menus[0].row]
api_resource = menus[1].rows[menus[1].row]
command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'"
bytes_list = subprocess.check_output(eval(command), shell=True).split()
resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
if not resources:
resources = [f"No resources found in {namespace} namespace.",]
else:
resources = ["No namespace selected",]
menu.rows = resources
for index, row in enumerate(menu.rows):
menu.win.addstr(index + 3, 2, row)
if menu.rows:
menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # помечаем выбранное меню
continue
match key_pressed:
case 'q':
running = False running = False
case '/': continue
search_mode = True match menu2.state:
case 'g': case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"): match menu3.state:
run_command("f'kubectl -n {namespace} get {api_resource} {resource} -o yaml | batcat -l yaml --paging always --style numbers'", current_menu) case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
case 'd': pass
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"): case MenuState.NOT_SELECTED_WITH_SEARCH:
run_command("f'kubectl -n {namespace} describe {api_resource} {resource} | batcat -l yaml --paging always --style numbers'", current_menu) pass
case 'l': case MenuState.NOT_SELECTED_WITH_SEARCH:
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources") and menus[1].rows[menus[1].row] == "pods": pass
run_command("f'kubectl -n {namespace} logs {resource} | batcat -l log --paging always --style numbers'", current_menu) case MenuState.SELECTED_WITH_SEARCH:
case 'e': match key_pressed:
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"): case "KEY_BACKSPACE":
run_command("f'kubectl edit {api_resource} -n {namespace} {resource}'", current_menu) if menu1.search_string:
case '\t' | "KEY_RIGHT": menu1.search_string = menu1.search_string[:-1] # удаляем символ из строки поиска
current_menu = navigate_horizontally("right", current_menu) filter_menu1()
case "KEY_BTAB" | "KEY_LEFT": else:
current_menu = navigate_horizontally("left", current_menu) menu1.win.addstr(curses.LINES - 2, 2, "Press / for search")
case "KEY_DOWN": menu1.win.box()
navigate_vertically("down", current_menu) menu1.win.refresh()
case "KEY_UP": menu1.state = MenuState.SELECTED_WITHOUT_SEARCH
navigate_vertically("up", current_menu) continue
case _:
if key_pressed.isalpha() or key_pressed == "-": # namespace не может иметь иных символов кроме a-z и -
menu1.search_string += key_pressed
filter_menu1()
match menu2.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
match menu2.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.SELECTED_WITHOUT_SEARCH:
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
match menu2.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.SELECTED_WITHOUT_SEARCH:
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.SELECTED_WITH_SEARCH:
pass
# if current_menu == 0:
# if key_pressed == "/":
# search_mode = True
# if search_mode:
# match key_pressed:
# case '\t' | "KEY_RIGHT":
# current_menu = navigate_horizontally("right", current_menu)
# case "KEY_BTAB" | "KEY_LEFT":
# current_menu = navigate_horizontally("left", current_menu)
# case "KEY_DOWN":
# navigate_vertically("down", current_menu)
# case "KEY_UP":
# navigate_vertically("up", current_menu)
# case "KEY_BACKSPACE":
# if search_string:
# search_string = search_string[:-1]
# else:
# search_mode = False
# if key_pressed.isalpha() or key_pressed == "-":
# search_string += key_pressed
# elif key_pressed == "/" and search_string == "":
# pass
# else:
# continue
# init_screen()
# for menu in menus:
# menu.win.clear() # очищаем окно
# menu.win.box()
# menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна
# if menu.name == "Namespaces":
# menu.rows = list(filter(lambda x: (x.startswith(search_string)), namespaces)) # меняем строки у окна Namespaces
# menu.row = 0
# if search_mode:
# menu.win.addstr(curses.LINES - 2, 2, f"/{search_string}")
# else:
# menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
# elif menu.name == "Resources":
# if menus[0].rows:
# namespace = menus[0].rows[menus[0].row]
# api_resource = menus[1].rows[menus[1].row]
# command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'"
# bytes_list = subprocess.check_output(eval(command), shell=True).split()
# resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
# if not resources:
# resources = [f"No resources found in {namespace} namespace.",]
# else:
# resources = ["No namespace selected",]
# menu.rows = resources
#
# for index, row in enumerate(menu.rows):
# menu.win.addstr(index + 3, 2, row)
# if menu.rows:
# menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # помечаем выбранное меню
# continue
#
# match key_pressed:
# case 'q':
# running = False
# case '/':
# search_mode = True
# case 'g':
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
# run_command("f'kubectl -n {namespace} get {api_resource} {resource} -o yaml | batcat -l yaml --paging always --style numbers'", current_menu)
# case 'd':
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
# run_command("f'kubectl -n {namespace} describe {api_resource} {resource} | batcat -l yaml --paging always --style numbers'", current_menu)
# case 'l':
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources") and menus[1].rows[menus[1].row] == "pods":
# run_command("f'kubectl -n {namespace} logs {resource} | batcat -l log --paging always --style numbers'", current_menu)
# case 'e':
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
# run_command("f'kubectl edit {api_resource} -n {namespace} {resource}'", current_menu)
# case '\t' | "KEY_RIGHT":
# current_menu = navigate_horizontally("right", current_menu)
# case "KEY_BTAB" | "KEY_LEFT":
# current_menu = navigate_horizontally("left", current_menu)
# case "KEY_DOWN":
# navigate_vertically("down", current_menu)
# case "KEY_UP":
# navigate_vertically("up", current_menu)
main(stdscr) main(stdscr)
curses.nocbreak() curses.nocbreak()