534 lines
21 KiB
Python
534 lines
21 KiB
Python
|
|
import subprocess
|
|||
|
|
import os
|
|||
|
|
import re
|
|||
|
|
import shlex
|
|||
|
|
from abc import ABC, abstractmethod
|
|||
|
|
from typing import Optional, List, Callable
|
|||
|
|
|
|||
|
|
from dialog import Dialog
|
|||
|
|
from postinstall.logger import logger
|
|||
|
|
|
|||
|
|
import socket
|
|||
|
|
|
|||
|
|
REGISTRY_URL = "dr.gosrv.ru"
|
|||
|
|
REGISTRY_LOGIN = "anon_user"
|
|||
|
|
REGISTRY_PASSWORD = "Qweasdzxc123C@"
|
|||
|
|
DOCKER_COMPOSE_FILE_PATH = "/usr/local/share/postinstall/portainer/"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CommandExecutor:
|
|||
|
|
@staticmethod
|
|||
|
|
def run_command(
|
|||
|
|
command: str,
|
|||
|
|
shell: bool = False,
|
|||
|
|
input_data: Optional[str] = None,
|
|||
|
|
ignore_errors: List[int] = [],
|
|||
|
|
) -> subprocess.CompletedProcess:
|
|||
|
|
try:
|
|||
|
|
return subprocess.run(
|
|||
|
|
command if shell else shlex.split(command),
|
|||
|
|
shell=shell,
|
|||
|
|
check=True,
|
|||
|
|
input=input_data,
|
|||
|
|
text=True,
|
|||
|
|
capture_output=True,
|
|||
|
|
)
|
|||
|
|
except subprocess.CalledProcessError as e:
|
|||
|
|
if e.returncode not in ignore_errors:
|
|||
|
|
logger.error(f"Ошибка выполнения команды: {e}")
|
|||
|
|
raise
|
|||
|
|
else:
|
|||
|
|
logger.warning(
|
|||
|
|
f"Команда завершилась с игнорируемым кодом ошибки {e.returncode}: {e}"
|
|||
|
|
)
|
|||
|
|
return e
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SystemChecker:
|
|||
|
|
@staticmethod
|
|||
|
|
def is_command_installed(command: str) -> bool:
|
|||
|
|
try:
|
|||
|
|
return CommandExecutor.run_command(f"which {command}").returncode == 0
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ProgressGauge:
|
|||
|
|
def __init__(self, dialog: Dialog, title: str, total_steps: int):
|
|||
|
|
self.dialog = dialog
|
|||
|
|
self.title = title
|
|||
|
|
self.total_steps = total_steps
|
|||
|
|
self.current_step = 0
|
|||
|
|
|
|||
|
|
def update(self, step: int, message: str):
|
|||
|
|
self.current_step = step
|
|||
|
|
percent = int((step / self.total_steps) * 100)
|
|||
|
|
self.dialog.gauge_update(percent, f"{message} ({percent}%)", update_text=True)
|
|||
|
|
|
|||
|
|
def __enter__(self):
|
|||
|
|
self.dialog.gauge_start(self.title, percent=0)
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|||
|
|
self.dialog.gauge_stop()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DockerManager:
|
|||
|
|
@staticmethod
|
|||
|
|
def install(progress_callback: Callable[[int, str], None]):
|
|||
|
|
logger.info("Установка Docker...")
|
|||
|
|
progress_callback(1, "Обновление пакетов")
|
|||
|
|
CommandExecutor.run_command("apt-get update")
|
|||
|
|
progress_callback(2, "Установка Docker")
|
|||
|
|
CommandExecutor.run_command("apt-get install -y docker.io")
|
|||
|
|
progress_callback(3, "Установка Docker Compose")
|
|||
|
|
CommandExecutor.run_command("apt-get install -y docker-compose")
|
|||
|
|
progress_callback(4, "Выполнение входа в Docker registry")
|
|||
|
|
DockerManager.login_to_registry()
|
|||
|
|
progress_callback(5, "Docker успешно установлен")
|
|||
|
|
logger.info("Docker успешно установлен")
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def uninstall(progress_callback: Callable[[int, str], None]):
|
|||
|
|
logger.info("Удаление Docker...")
|
|||
|
|
progress_callback(1, "Удаление Docker и Docker Compose")
|
|||
|
|
CommandExecutor.run_command("apt-get remove -y docker.io docker-compose")
|
|||
|
|
progress_callback(2, "Очистка зависимостей")
|
|||
|
|
CommandExecutor.run_command("apt-get autoremove -y")
|
|||
|
|
progress_callback(3, "Удаление файлов Docker")
|
|||
|
|
CommandExecutor.run_command("rm -rf /var/run/docker")
|
|||
|
|
CommandExecutor.run_command("rm -rf /var/lib/docker")
|
|||
|
|
progress_callback(4, "Docker успешно удален")
|
|||
|
|
logger.info("Docker успешно удален")
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def login_to_registry():
|
|||
|
|
logger.info("Выполняется вход в Docker registry...")
|
|||
|
|
try:
|
|||
|
|
result = CommandExecutor.run_command(
|
|||
|
|
f"echo {REGISTRY_PASSWORD} | docker login --username {REGISTRY_LOGIN} --password-stdin {REGISTRY_URL}",
|
|||
|
|
shell=True,
|
|||
|
|
)
|
|||
|
|
logger.info("Успешный вход в Docker registry")
|
|||
|
|
logger.debug(f"Docker login output: {result.stdout}")
|
|||
|
|
except subprocess.CalledProcessError as e:
|
|||
|
|
logger.error(f"Ошибка входа в Docker registry: {e}")
|
|||
|
|
logger.error(f"Stderr: {e.stderr}")
|
|||
|
|
raise
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UFWManager:
|
|||
|
|
@staticmethod
|
|||
|
|
def add_rule(port: str):
|
|||
|
|
rule_name = "portainer"
|
|||
|
|
logger.info(
|
|||
|
|
f"Добавление именованного правила UFW '{rule_name}' для порта {port}..."
|
|||
|
|
)
|
|||
|
|
CommandExecutor.run_command(f"sudo ufw allow {port}/tcp comment '{rule_name}'")
|
|||
|
|
CommandExecutor.run_command(f"sudo ufw app update {rule_name}")
|
|||
|
|
logger.info(
|
|||
|
|
f"Именованное правило UFW '{rule_name}' для порта {port} успешно добавлено"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def remove_rule(port: str):
|
|||
|
|
rule_name = "portainer"
|
|||
|
|
logger.info(
|
|||
|
|
f"Удаление именованного правила UFW '{rule_name}' для порта {port}..."
|
|||
|
|
)
|
|||
|
|
CommandExecutor.run_command(f"sudo ufw delete allow {port}/tcp")
|
|||
|
|
CommandExecutor.run_command(f"sudo ufw app update {rule_name}")
|
|||
|
|
logger.info(
|
|||
|
|
f"Именованное правило UFW '{rule_name}' для порта {port} успешно удалено"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PortainerManager:
|
|||
|
|
@staticmethod
|
|||
|
|
def install(
|
|||
|
|
port: str, password: str, ip: str, progress_callback: Callable[[int, str], None]
|
|||
|
|
):
|
|||
|
|
logger.info("Установка Portainer...")
|
|||
|
|
progress_callback(1, "Вход в Docker registry")
|
|||
|
|
DockerManager.login_to_registry()
|
|||
|
|
progress_callback(2, "Остановка предыдущей версии Portainer")
|
|||
|
|
try:
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
f"sudo docker-compose -f {DOCKER_COMPOSE_FILE_PATH}docker-compose.yml down",
|
|||
|
|
ignore_errors=[1],
|
|||
|
|
)
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
progress_callback(3, "Удаление старых данных Portainer")
|
|||
|
|
try:
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
"docker volume ls -q -f name=portainer* | xargs -r docker volume rm",
|
|||
|
|
ignore_errors=[1],
|
|||
|
|
)
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
progress_callback(4, "Установка apache2-utils для htpasswd")
|
|||
|
|
if not SystemChecker.is_command_installed("htpasswd"):
|
|||
|
|
CommandExecutor.run_command("apt-get install -y apache2-utils")
|
|||
|
|
|
|||
|
|
progress_callback(5, "Создание конфигурации Portainer")
|
|||
|
|
os.makedirs(os.path.dirname(DOCKER_COMPOSE_FILE_PATH), exist_ok=True)
|
|||
|
|
password_hash = PortainerManager._generate_password_hash(password)
|
|||
|
|
PortainerManager._create_docker_compose_file(port, password_hash, ip)
|
|||
|
|
|
|||
|
|
progress_callback(6, "Запуск Portainer")
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
f"sudo docker-compose -f {DOCKER_COMPOSE_FILE_PATH}docker-compose.yml up -d"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# if ip != "127.0.0.1":
|
|||
|
|
if True:
|
|||
|
|
progress_callback(7, "Добавление правила UFW")
|
|||
|
|
UFWManager.add_rule(port)
|
|||
|
|
|
|||
|
|
progress_callback(8, "Portainer успешно установлен")
|
|||
|
|
logger.info("Portainer успешно установлен")
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def uninstall(progress_callback: Callable[[int, str], None]):
|
|||
|
|
logger.info("Удаление Portainer...")
|
|||
|
|
progress_callback(1, "Остановка Portainer")
|
|||
|
|
try:
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
f"docker-compose -f {DOCKER_COMPOSE_FILE_PATH}docker-compose.yml down --volumes",
|
|||
|
|
ignore_errors=[1],
|
|||
|
|
)
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
pass
|
|||
|
|
progress_callback(2, "Удаление файлов конфигурации")
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
f"rm -f {DOCKER_COMPOSE_FILE_PATH}docker-compose.yml"
|
|||
|
|
)
|
|||
|
|
# try:
|
|||
|
|
# with open(f"{DOCKER_COMPOSE_FILE_PATH}docker-compose.yml", "r") as f:
|
|||
|
|
# config = f.read()
|
|||
|
|
# port_ip_match = re.search(r'"(.+):(\d+):9000"', config)
|
|||
|
|
# if port_ip_match:
|
|||
|
|
# ip, port = port_ip_match.groups()
|
|||
|
|
# # if True:
|
|||
|
|
# # progress_callback(3, "Удаление правила UFW")
|
|||
|
|
# # UFWManager.remove_rule(port)
|
|||
|
|
# except FileNotFoundError:
|
|||
|
|
# logger.warning("Файл конфигурации не найден. Пропуск удаления правила UFW.")
|
|||
|
|
|
|||
|
|
progress_callback(4, "Portainer успешно удален")
|
|||
|
|
logger.info("Portainer успешно удален")
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _generate_password_hash(password: str) -> str:
|
|||
|
|
return CommandExecutor.run_command(
|
|||
|
|
f"htpasswd -nbB admin {shlex.quote(password)} | cut -d ':' -f 2 | sed 's/\\$/\\$\\$/g'",
|
|||
|
|
shell=True,
|
|||
|
|
).stdout.strip()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _create_docker_compose_file(port: str, password_hash: str, ip: str):
|
|||
|
|
docker_compose_content = f"""
|
|||
|
|
version: '2'
|
|||
|
|
|
|||
|
|
services:
|
|||
|
|
portainer:
|
|||
|
|
image: {REGISTRY_URL}/base_images/portainer:latest
|
|||
|
|
ports:
|
|||
|
|
- "{ip}:{port}:9000"
|
|||
|
|
command: --admin-password '{password_hash}'
|
|||
|
|
networks:
|
|||
|
|
- local
|
|||
|
|
volumes:
|
|||
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|||
|
|
- portainer-data:/data
|
|||
|
|
environment:
|
|||
|
|
- PORTAINER_LOCAL_IMAGES_ENVIRONMENT_ID=1
|
|||
|
|
deploy:
|
|||
|
|
mode: global
|
|||
|
|
|
|||
|
|
networks:
|
|||
|
|
local:
|
|||
|
|
driver: bridge
|
|||
|
|
|
|||
|
|
volumes:
|
|||
|
|
portainer-data:
|
|||
|
|
"""
|
|||
|
|
with open(f"{DOCKER_COMPOSE_FILE_PATH}docker-compose.yml", "w") as f:
|
|||
|
|
f.write(docker_compose_content)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InputValidator:
|
|||
|
|
@staticmethod
|
|||
|
|
def validate(value: str, pattern: str) -> bool:
|
|||
|
|
return bool(re.match(pattern, value))
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UIElement(ABC):
|
|||
|
|
def __init__(self, dialog: Dialog):
|
|||
|
|
self.dialog = dialog
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
def show(self):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Menu(UIElement):
|
|||
|
|
def show(self):
|
|||
|
|
choices = []
|
|||
|
|
if not SystemChecker.is_command_installed("docker"):
|
|||
|
|
choices.append(("1", "Установить Docker"))
|
|||
|
|
else:
|
|||
|
|
choices.append(("1", "Удалить Docker"))
|
|||
|
|
|
|||
|
|
if SystemChecker.is_command_installed("docker"):
|
|||
|
|
if not os.path.exists(f"{DOCKER_COMPOSE_FILE_PATH}docker-compose.yml"):
|
|||
|
|
choices.append(("2", "Установить Portainer"))
|
|||
|
|
else:
|
|||
|
|
choices.append(("2", "Удалить Portainer"))
|
|||
|
|
|
|||
|
|
code, choice = self.dialog.menu("Выберите действие:", choices=choices)
|
|||
|
|
return code, choice
|
|||
|
|
|
|||
|
|
|
|||
|
|
class YesNoUI(UIElement):
|
|||
|
|
def show(self, text: str):
|
|||
|
|
return self.dialog.yesno(text) == self.dialog.OK
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PasswordInput(UIElement):
|
|||
|
|
def show(self):
|
|||
|
|
while True:
|
|||
|
|
code, password = self.dialog.passwordbox(
|
|||
|
|
"Введите пароль для Portainer:", insecure=True
|
|||
|
|
)
|
|||
|
|
if code == self.dialog.CANCEL:
|
|||
|
|
return None
|
|||
|
|
if InputValidator.validate(password, r"^.{12,}$"):
|
|||
|
|
return password
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Пароль должен содержать минимум 12 символов.", width=50, height=10
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PortInput(UIElement):
|
|||
|
|
def show(self):
|
|||
|
|
while True:
|
|||
|
|
code, port = self.dialog.inputbox(
|
|||
|
|
"Введите порт для Portainer:", init="9000"
|
|||
|
|
)
|
|||
|
|
if code == self.dialog.CANCEL:
|
|||
|
|
return None
|
|||
|
|
if InputValidator.validate(port, r"^[1-9][0-9]{0,4}$"):
|
|||
|
|
return port
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Некорректный порт. Введите число от 1 до 65535.", width=50, height=10
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class IPInput(UIElement):
|
|||
|
|
def get_default_interface_ip(self):
|
|||
|
|
try:
|
|||
|
|
# Выполнение команды для получения IP-адреса основного интерфейса
|
|||
|
|
command = "ip -o -4 addr show $(ip route | grep default | awk '{print $5}') | awk '{print $4}' | cut -d/ -f1"
|
|||
|
|
result = subprocess.check_output(command, shell=True, text=True)
|
|||
|
|
return result.strip() # Возвращаем IP-адрес без лишних символов
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return "127.0.0.1" # В случае ошибки вернём стандартный IP
|
|||
|
|
|
|||
|
|
def show(self):
|
|||
|
|
# Получаем IP-адрес основного сетевого интерфейса
|
|||
|
|
default_ip = self.get_default_interface_ip()
|
|||
|
|
|
|||
|
|
while True:
|
|||
|
|
# Используем полученный IP-адрес как начальное значение
|
|||
|
|
code, ip = self.dialog.inputbox(
|
|||
|
|
"Введите IP адрес для Portainer:", init=default_ip
|
|||
|
|
)
|
|||
|
|
if code == self.dialog.CANCEL:
|
|||
|
|
return None
|
|||
|
|
if InputValidator.validate(ip, r"^(\d{1,3}\.){3}\d{1,3}$"):
|
|||
|
|
return ip
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Некорректный IP адрес. Введите адрес в формате X.X.X.X",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NetworkInterfaceInput(UIElement):
|
|||
|
|
def get_network_interfaces(self):
|
|||
|
|
try:
|
|||
|
|
# Get all network interfaces
|
|||
|
|
interfaces = subprocess.check_output(
|
|||
|
|
"ip -o link show", shell=True, text=True
|
|||
|
|
)
|
|||
|
|
# Extract interface names and their IP addresses
|
|||
|
|
interface_list = []
|
|||
|
|
for line in interfaces.split("\n"):
|
|||
|
|
if line:
|
|||
|
|
interface = line.split(":")[1].strip()
|
|||
|
|
ip = self.get_ip_address(interface)
|
|||
|
|
if ip:
|
|||
|
|
interface_list.append((interface, ip))
|
|||
|
|
return interface_list
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def get_ip_address(self, interface):
|
|||
|
|
try:
|
|||
|
|
ip = subprocess.check_output(
|
|||
|
|
f"ip -o -4 addr list {interface}", shell=True, text=True
|
|||
|
|
)
|
|||
|
|
return ip.split()[3].split("/")[0]
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def show(self):
|
|||
|
|
interfaces = self.get_network_interfaces()
|
|||
|
|
if not interfaces:
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Не удалось получить список сетевых интерфейсов.", width=50, height=10
|
|||
|
|
)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
choices = [(f"{interface} ({ip})", interface) for interface, ip in interfaces]
|
|||
|
|
code, choice = self.dialog.menu(
|
|||
|
|
"Выберите сетевой интерфейс для Docker Swarm:", choices=choices
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if code == self.dialog.OK:
|
|||
|
|
return choice
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DockerConfigUI:
|
|||
|
|
def __init__(self):
|
|||
|
|
self.dialog = Dialog(dialog="dialog")
|
|||
|
|
self.menu = Menu(self.dialog)
|
|||
|
|
self.password_input = PasswordInput(self.dialog)
|
|||
|
|
self.port_input = PortInput(self.dialog)
|
|||
|
|
self.ip_input = IPInput(self.dialog)
|
|||
|
|
self.network_interface_input = NetworkInterfaceInput(self.dialog)
|
|||
|
|
|
|||
|
|
def run(self):
|
|||
|
|
try:
|
|||
|
|
while True:
|
|||
|
|
code, choice = self.menu.show()
|
|||
|
|
if code == self.dialog.OK:
|
|||
|
|
if choice == "1":
|
|||
|
|
self._handle_docker()
|
|||
|
|
elif choice == "2":
|
|||
|
|
self.handle_portainer()
|
|||
|
|
elif choice == "3":
|
|||
|
|
break
|
|||
|
|
else:
|
|||
|
|
break
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Произошла ошибка: {e}")
|
|||
|
|
self.dialog.msgbox(f"Произошла ошибка: {str(e)}", width=50, height=10)
|
|||
|
|
|
|||
|
|
def _handle_docker(self):
|
|||
|
|
if not SystemChecker.is_command_installed("docker"):
|
|||
|
|
with ProgressGauge(self.dialog, "Установка Docker", 4) as gauge:
|
|||
|
|
DockerManager.install(gauge.update)
|
|||
|
|
self.dialog.msgbox("Docker успешно установлен.", width=50, height=10)
|
|||
|
|
logger.info("Docker успешно установлен.")
|
|||
|
|
yes_no_ui = YesNoUI(self.dialog)
|
|||
|
|
if yes_no_ui.show(
|
|||
|
|
text="Вы хотите установить Docker Swarm - инструмент для управления кластером докер серверов?"
|
|||
|
|
):
|
|||
|
|
interface = self.network_interface_input.show()
|
|||
|
|
if interface:
|
|||
|
|
ip = interface.split("(")[1].split(")")[0]
|
|||
|
|
if ip:
|
|||
|
|
try:
|
|||
|
|
CommandExecutor.run_command(
|
|||
|
|
f"docker swarm init --advertise-addr {ip}"
|
|||
|
|
)
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
f"Docker Swarm успешно инициализирован на интерфейсе {interface}",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
except subprocess.CalledProcessError as e:
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
f"Ошибка при инициализации Docker Swarm: {str(e)}",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
f"Не удалось получить IP-адрес для интерфейса {interface}.",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Инициализация Docker Swarm отменена.", width=50, height=10
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if yes_no_ui.show(
|
|||
|
|
text="Вы хотите установить Portainer - удобный Web интерфейс для управления докер контейнерами?"
|
|||
|
|
):
|
|||
|
|
self.handle_portainer()
|
|||
|
|
else:
|
|||
|
|
with ProgressGauge(self.dialog, "Удаление Docker", 4) as gauge:
|
|||
|
|
PortainerManager.uninstall(gauge.update)
|
|||
|
|
DockerManager.uninstall(gauge.update)
|
|||
|
|
self.dialog.msgbox("Docker успешно удален.", width=50, height=10)
|
|||
|
|
logger.info("Docker успешно удален.")
|
|||
|
|
|
|||
|
|
def handle_portainer(self):
|
|||
|
|
if not SystemChecker.is_command_installed("docker"):
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
"Docker не установлен. Пожалуйста, сначала установите Docker.",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
if not os.path.exists(f"{DOCKER_COMPOSE_FILE_PATH}docker-compose.yml"):
|
|||
|
|
self._install_portainer()
|
|||
|
|
else:
|
|||
|
|
with ProgressGauge(self.dialog, "Удаление Portainer", 3) as gauge:
|
|||
|
|
PortainerManager.uninstall(gauge.update)
|
|||
|
|
self.dialog.msgbox("Portainer успешно удален.", width=50, height=10)
|
|||
|
|
logger.info("Portainer успешно удален.")
|
|||
|
|
|
|||
|
|
def _install_portainer(self):
|
|||
|
|
password = self.password_input.show()
|
|||
|
|
if not password:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
port = self.port_input.show()
|
|||
|
|
if not port:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
ip = self.ip_input.show()
|
|||
|
|
if not ip:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
with ProgressGauge(self.dialog, "Установка Portainer", 6) as gauge:
|
|||
|
|
PortainerManager.install(port, password, ip, gauge.update)
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
f"Portainer успешно установлен.\nВы можете войти в Portainer по адресу {ip}:{port} используя логин admin и введенный пароль",
|
|||
|
|
width=50,
|
|||
|
|
height=10,
|
|||
|
|
)
|
|||
|
|
logger.info(
|
|||
|
|
f"Portainer успешно установлен.\nВы можете войти в Portainer по адресу {ip}:{port} используя логин admin и введенный пароль"
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
self.dialog.msgbox(
|
|||
|
|
f"Ошибка при установке Portainer: {str(e)}", width=50, height=10
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run():
|
|||
|
|
ui = DockerConfigUI()
|
|||
|
|
ui.run()
|