# ============================================================================= # Ansible Template - Универсальная лаборатория для тестирования Ansible ролей # Автор: Сергей Антропов # Сайт: https://devops.org.ru # ============================================================================= # ============================================================================= # ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ # ============================================================================= # Основные переменные PROJECT_NAME ?= ansible-template VERSION ?= 0.1.0 AUTHOR ?= "Сергей Антропов" SITE ?= "https://devops.org.ru" # Docker переменные DOCKER_IMAGE ?= quay.io/ansible/creator-ee:latest DOCKER_COMPOSE ?= docker compose DOCKER_NETWORK ?= labnet # Molecule переменные SCENARIO ?= universal LAB_SPEC ?= molecule/presets/minimal.yml MOLECULE_EPHEMERAL_DIRECTORY ?= /tmp/molecule # Kubernetes переменные KUBE_CONTEXT ?= kind-lab ISTIO_VERSION ?= 1.22.1 KIND_VERSION ?= v0.23.0 # Переменные окружения ENV_FILE ?= .env ROLES_DIR ?= ./roles VAULT_PASSWORD_FILE ?= vault/.vault # Цвета для вывода RED := \033[0;31m GREEN := \033[0;32m YELLOW := \033[0;33m BLUE := \033[0;34m PURPLE := \033[0;35m CYAN := \033[0;36m WHITE := \033[0;37m RESET := \033[0m # ============================================================================= # ОСНОВНЫЕ КОМАНДЫ # ============================================================================= # Команда по умолчанию - интерактивное меню .DEFAULT_GOAL := check-init-and-menu .PHONY: help help: ## Показать справку по всем командам @echo "$(CYAN)Ansible Template - Универсальная лаборатория$(RESET)" @echo "$(YELLOW)Автор: $(AUTHOR)$(RESET)" @echo "$(YELLOW)Сайт: $(SITE)$(RESET)" @echo "" @echo "$(GREEN)Доступные команды:$(RESET)" @echo " $(CYAN)make lab <команда>$(RESET) - Управление лабораторией (up|down|test|destroy)" @echo " $(CYAN)make kube <команда>$(RESET) - Управление Kubernetes (logs|exec|port-forward)" @echo " $(CYAN)make preset <команда>$(RESET) - Управление пресетами (list|create|test)" @echo " $(CYAN)make role <команда>$(RESET) - Управление ролями (list|create|test|lint|playbook)" @echo " $(CYAN)make vault <команда>$(RESET) - Управление vault (view|create|edit|encrypt)" @echo " $(CYAN)make report$(RESET) - Генерация HTML отчета" @echo " $(CYAN)make lint$(RESET) - Проверка синтаксиса" @echo " $(CYAN)make snapshot$(RESET) - Создание снимка лаборатории" @echo " $(CYAN)make restore$(RESET) - Восстановление из снимка" @echo " $(CYAN)make cleanup$(RESET) - Очистка всех данных" @echo "" @echo "$(GREEN)Интерактивные команды:$(RESET)" @echo " $(CYAN)make preset-create-interactive$(RESET) - Интерактивное создание пресета" @echo " $(CYAN)make role-create-interactive$(RESET) - Интерактивное создание роли" @echo "" @echo "$(GREEN)CI/CD команды:$(RESET)" @echo " $(CYAN)make ci-test$(RESET) - Запустить все тесты" @echo " $(CYAN)make ci-lint$(RESET) - Проверить синтаксис" @echo " $(CYAN)make ci-deploy$(RESET) - Развертывание" @echo " $(CYAN)make ci-cleanup$(RESET) - Очистка после тестов" @echo " $(CYAN)make ci-full$(RESET) - Полный цикл (lint + test + deploy)" @echo " $(CYAN)make ci-security$(RESET) - Проверка безопасности" @echo " $(CYAN)make ci-validate$(RESET) - Валидация проекта" @echo " $(CYAN)make ci-report$(RESET) - Генерация отчета" @echo " $(CYAN)make ci-all$(RESET) - Все проверки" @echo "" @echo "$(YELLOW)Подробная документация: docs/$(RESET)" # ============================================================================= # CI/CD КОМАНДЫ # ============================================================================= .PHONY: ci-test ci-test: ## CI/CD: Запустить все тесты @echo "$(BLUE)🧪 CI/CD: Запуск всех тестов$(RESET)" @echo "$(YELLOW)Тестирование лаборатории...$(RESET)" @if make lab test; then \ echo "$(GREEN)✅ Тесты лаборатории прошли успешно$(RESET)"; \ else \ echo "$(RED)❌ Тесты лаборатории не прошли$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Тестирование ролей...$(RESET)" @if make role test; then \ echo "$(GREEN)✅ Тесты ролей прошли успешно$(RESET)"; \ else \ echo "$(RED)❌ Тесты ролей не прошли$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)✅ Все тесты прошли успешно$(RESET)" .PHONY: ci-lint ci-lint: ## CI/CD: Проверить синтаксис всех файлов @echo "$(BLUE)🔍 CI/CD: Проверка синтаксиса$(RESET)" @echo "$(YELLOW)Проверка YAML синтаксиса...$(RESET)" @if find . -name "*.yml" -o -name "*.yaml" | grep -v node_modules | xargs -I {} sh -c 'python3 -c "import yaml; yaml.safe_load(open(\"{}\"))" 2>/dev/null || echo "Ошибка в файле: {}"'; then \ echo "$(GREEN)✅ YAML синтаксис корректен$(RESET)"; \ else \ echo "$(RED)❌ Ошибки в YAML синтаксисе$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Проверка Ansible синтаксиса...$(RESET)" @if make lint >/dev/null 2>&1; then \ echo "$(GREEN)✅ Ansible синтаксис корректен$(RESET)"; \ else \ echo "$(YELLOW)⚠️ Ansible lint предупреждения (не критично)$(RESET)"; \ fi @echo "$(GREEN)✅ Все проверки синтаксиса прошли успешно$(RESET)" .PHONY: ci-deploy ci-deploy: ## CI/CD: Развертывание в лаборатории @echo "$(BLUE)🚀 CI/CD: Развертывание$(RESET)" @echo "$(YELLOW)Создание лаборатории...$(RESET)" @if make lab up; then \ echo "$(GREEN)✅ Лаборатория создана$(RESET)"; \ else \ echo "$(RED)❌ Ошибка создания лаборатории$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Развертывание ролей...$(RESET)" @if make role deploy; then \ echo "$(GREEN)✅ Роли развернуты$(RESET)"; \ else \ echo "$(RED)❌ Ошибка развертывания ролей$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Проверка развертывания...$(RESET)" @if make lab test; then \ echo "$(GREEN)✅ Развертывание проверено$(RESET)"; \ else \ echo "$(RED)❌ Ошибка проверки развертывания$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)✅ Развертывание завершено успешно$(RESET)" .PHONY: ci-cleanup ci-cleanup: ## CI/CD: Очистка после тестов @echo "$(BLUE)🧹 CI/CD: Очистка$(RESET)" @echo "$(YELLOW)Остановка лаборатории...$(RESET)" @make lab down || true @echo "$(YELLOW)Очистка данных...$(RESET)" @make cleanup || true @echo "$(GREEN)✅ Очистка завершена$(RESET)" .PHONY: ci-full ci-full: ci-lint ci-test ci-deploy ## CI/CD: Полный цикл (lint + test + deploy) @echo "$(GREEN)✅ CI/CD: Полный цикл завершен успешно$(RESET)" .PHONY: ci-report ci-report: ## CI/CD: Генерация отчета @echo "$(BLUE)📊 CI/CD: Генерация отчета$(RESET)" @if make report; then \ echo "$(GREEN)✅ Отчет сгенерирован$(RESET)"; \ echo "$(YELLOW)Отчет доступен в: reports/$(RESET)"; \ else \ echo "$(RED)❌ Ошибка генерации отчета$(RESET)"; \ exit 1; \ fi .PHONY: ci-security ci-security: ## CI/CD: Проверка безопасности @echo "$(BLUE)🔒 CI/CD: Проверка безопасности$(RESET)" @echo "$(YELLOW)Проверка vault файлов...$(RESET)" @if make vault view >/dev/null 2>&1; then \ echo "$(GREEN)✅ Vault файлы корректны$(RESET)"; \ else \ echo "$(RED)❌ Ошибки в vault файлах$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Проверка секретов...$(RESET)" @if make check-secrets; then \ echo "$(GREEN)✅ Секреты защищены$(RESET)"; \ else \ echo "$(RED)❌ Найдены незащищенные секреты$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)✅ Проверка безопасности завершена$(RESET)" .PHONY: ci-validate ci-validate: ## CI/CD: Валидация проекта @echo "$(BLUE)✅ CI/CD: Валидация проекта$(RESET)" @echo "$(YELLOW)Проверка структуры проекта...$(RESET)" @if [ -f .env ] && [ -f vault/.vault ] && [ -d molecule/presets ]; then \ echo "$(GREEN)✅ Структура проекта корректна$(RESET)"; \ else \ echo "$(RED)❌ Неправильная структура проекта$(RESET)"; \ exit 1; \ fi @echo "$(YELLOW)Проверка зависимостей...$(RESET)" @if command -v docker >/dev/null 2>&1 && command -v whiptail >/dev/null 2>&1; then \ echo "$(GREEN)✅ Зависимости установлены$(RESET)"; \ else \ echo "$(RED)❌ Отсутствуют необходимые зависимости$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)✅ Валидация проекта завершена$(RESET)" .PHONY: ci-all ci-all: ci-validate ci-lint ci-test ci-deploy ci-security ci-report ## CI/CD: Все проверки @echo "$(GREEN)✅ CI/CD: Все проверки завершены успешно$(RESET)" .PHONY: menu menu: check-whiptail check-init ## Интерактивное главное меню @while true; do \ CHOICE=$$(whiptail --title "Ansible Template - Универсальная лаборатория" \ --menu "Выберите действие:" 20 60 12 \ "lab" "Управление лабораторией" \ "kube" "Управление Kubernetes" \ "preset" "Управление пресетами" \ "role" "Управление ролями" \ "vault" "Управление vault" \ "report" "Генерация отчетов" \ "lint" "Проверка синтаксиса" \ "snapshot" "Снимки лаборатории" \ "cleanup" "Очистка данных" \ "docs" "Документация" \ "help" "Помощь и справка" \ "exit" "Выход" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "exit" ]; then \ echo "$(YELLOW)👋 До свидания!$(RESET)"; \ break; \ fi; \ case "$$CHOICE" in \ "lab") make menu-lab;; \ "kube") make menu-kube;; \ "preset") make menu-preset;; \ "role") make menu-role;; \ "vault") make menu-vault;; \ "report") make menu-report;; \ "lint") make menu-lint;; \ "snapshot") make menu-snapshot;; \ "cleanup") make menu-cleanup;; \ "docs") make menu-docs;; \ "help") make menu-help;; \ esac; \ done # ============================================================================= # ПРОВЕРКИ ИНИЦИАЛИЗАЦИИ # ============================================================================= .PHONY: check-init-and-menu check-init-and-menu: check-init menu ## Проверить инициализацию и запустить меню .PHONY: check-init check-init: ## Проверить инициализацию проекта @if [ ! -f .env ] || [ ! -f vault/.vault ] || [ ! -d molecule/presets ]; then \ echo "$(YELLOW)🔧 Первый запуск проекта - требуется инициализация$(RESET)"; \ echo "$(CYAN)🚀 Добро пожаловать в Ansible Template!$(RESET)"; \ echo "$(BLUE)Добро пожаловать в универсальную лабораторию для тестирования Ansible ролей!$(RESET)"; \ echo "$(BLUE)Для начала работы необходимо выполнить инициализацию проекта.$(RESET)"; \ echo "$(BLUE)Это займет всего несколько минут и включает:$(RESET)"; \ echo "$(BLUE)• Настройку переменных окружения$(RESET)"; \ echo "$(BLUE)• Создание vault файлов$(RESET)"; \ echo "$(BLUE)• Подготовку пресетов лаборатории$(RESET)"; \ echo ""; \ echo "$(YELLOW)Нажмите Enter для начала инициализации...$(RESET)"; \ read -r; \ make init-interactive; \ fi .PHONY: init-interactive init-interactive: ## Интерактивная инициализация проекта @echo "$(CYAN)🚀 Инициализация проекта Ansible Template$(RESET)"; \ echo "$(BLUE)📝 Настройка основных параметров проекта$(RESET)"; \ echo "$(YELLOW)Используйте значения по умолчанию (нажмите Enter) или введите свои значения$(RESET)"; \ echo ""; \ echo -n "$(BLUE)Название проекта [ansible-template]: $(RESET)"; \ read -r PROJECT_NAME; \ PROJECT_NAME=$${PROJECT_NAME:-ansible-template}; \ echo -n "$(BLUE)Версия проекта [0.1.0]: $(RESET)"; \ read -r VERSION; \ VERSION=$${VERSION:-0.1.0}; \ echo -n "$(BLUE)Автор проекта [Сергей Антропов]: $(RESET)"; \ read -r AUTHOR; \ AUTHOR=$${AUTHOR:-Сергей Антропов}; \ echo -n "$(BLUE)Сайт проекта [https://devops.org.ru]: $(RESET)"; \ read -r SITE; \ SITE=$${SITE:-https://devops.org.ru}; \ echo ""; \ echo "$(BLUE)🐳 Настройка Docker$(RESET)"; \ echo -n "$(BLUE)Docker образ для Ansible [quay.io/ansible/creator-ee:latest]: $(RESET)"; \ read -r DOCKER_IMAGE; \ DOCKER_IMAGE=$${DOCKER_IMAGE:-quay.io/ansible/creator-ee:latest}; \ echo -n "$(BLUE)Docker сеть [labnet]: $(RESET)"; \ read -r DOCKER_NETWORK; \ DOCKER_NETWORK=$${DOCKER_NETWORK:-labnet}; \ echo ""; \ echo "$(BLUE)🧪 Настройка лаборатории$(RESET)"; \ echo -n "$(BLUE)Сценарий Molecule [universal]: $(RESET)"; \ read -r SCENARIO; \ SCENARIO=$${SCENARIO:-universal}; \ echo -n "$(BLUE)Пресет лаборатории [molecule/presets/minimal.yml]: $(RESET)"; \ read -r LAB_SPEC; \ LAB_SPEC=$${LAB_SPEC:-molecule/presets/minimal.yml}; \ echo ""; \ echo "$(BLUE)☸️ Настройка Kubernetes$(RESET)"; \ echo -n "$(BLUE)Kubernetes контекст [kind-lab]: $(RESET)"; \ read -r KUBE_CONTEXT; \ KUBE_CONTEXT=$${KUBE_CONTEXT:-kind-lab}; \ echo -n "$(BLUE)Версия Istio [1.22.1]: $(RESET)"; \ read -r ISTIO_VERSION; \ ISTIO_VERSION=$${ISTIO_VERSION:-1.22.1}; \ echo -n "$(BLUE)Версия Kind [v0.23.0]: $(RESET)"; \ read -r KIND_VERSION; \ KIND_VERSION=$${KIND_VERSION:-v0.23.0}; \ echo ""; \ echo "$(BLUE)📁 Настройка путей$(RESET)"; \ echo -n "$(BLUE)Папка ролей [./roles]: $(RESET)"; \ read -r ROLES_DIR; \ ROLES_DIR=$${ROLES_DIR:-./roles}; \ echo -n "$(BLUE)Файл пароля vault [vault/.vault]: $(RESET)"; \ read -r VAULT_PASSWORD_FILE; \ VAULT_PASSWORD_FILE=$${VAULT_PASSWORD_FILE:-vault/.vault}; \ echo ""; \ echo "$(BLUE)📝 Создание файла .env$(RESET)"; \ echo "# Ansible Template - Универсальная лаборатория" > .env; \ echo "# Автор: $$AUTHOR" >> .env; \ echo "# Сайт: $$SITE" >> .env; \ echo "# Версия: $$VERSION" >> .env; \ echo "" >> .env; \ echo "# Основные переменные" >> .env; \ echo "PROJECT_NAME=$$PROJECT_NAME" >> .env; \ echo "VERSION=$$VERSION" >> .env; \ echo "AUTHOR=$$AUTHOR" >> .env; \ echo "SITE=$$SITE" >> .env; \ echo "" >> .env; \ echo "# Docker переменные" >> .env; \ echo "DOCKER_IMAGE=$$DOCKER_IMAGE" >> .env; \ echo "DOCKER_COMPOSE=docker compose" >> .env; \ echo "DOCKER_NETWORK=$$DOCKER_NETWORK" >> .env; \ echo "" >> .env; \ echo "# Molecule переменные" >> .env; \ echo "SCENARIO=$$SCENARIO" >> .env; \ echo "LAB_SPEC=$$LAB_SPEC" >> .env; \ echo "MOLECULE_EPHEMERAL_DIRECTORY=/tmp/molecule" >> .env; \ echo "" >> .env; \ echo "# Kubernetes переменные" >> .env; \ echo "KUBE_CONTEXT=$$KUBE_CONTEXT" >> .env; \ echo "ISTIO_VERSION=$$ISTIO_VERSION" >> .env; \ echo "KIND_VERSION=$$KIND_VERSION" >> .env; \ echo "" >> .env; \ echo "# Переменные окружения" >> .env; \ echo "ENV_FILE=.env" >> .env; \ echo "ROLES_DIR=$$ROLES_DIR" >> .env; \ echo "VAULT_PASSWORD_FILE=$$VAULT_PASSWORD_FILE" >> .env; \ echo "$(BLUE)🔐 Создание vault файлов$(RESET)"; \ mkdir -p vault; \ echo "ansible-vault-password" > vault/.vault; \ echo "$(BLUE)📋 Создание пресетов лаборатории$(RESET)"; \ mkdir -p molecule/presets; \ echo "---" > molecule/presets/minimal.yml; \ echo "# Минимальный пресет лаборатории" >> molecule/presets/minimal.yml; \ echo "# Автор: $$AUTHOR" >> molecule/presets/minimal.yml; \ echo "# Сайт: $$SITE" >> molecule/presets/minimal.yml; \ echo "" >> molecule/presets/minimal.yml; \ echo "hosts:" >> molecule/presets/minimal.yml; \ echo " - name: host1" >> molecule/presets/minimal.yml; \ echo " family: debian" >> molecule/presets/minimal.yml; \ echo " groups: [all]" >> molecule/presets/minimal.yml; \ echo "" >> molecule/presets/minimal.yml; \ echo "features:" >> molecule/presets/minimal.yml; \ echo " docker: true" >> molecule/presets/minimal.yml; \ echo " dind: false" >> molecule/presets/minimal.yml; \ echo " k8s: false" >> molecule/presets/minimal.yml; \ echo " istio: false" >> molecule/presets/minimal.yml; \ echo " monitoring: false" >> molecule/presets/minimal.yml; \ echo " chaos: false" >> molecule/presets/minimal.yml; \ echo "$(BLUE)📁 Создание необходимых папок$(RESET)"; \ mkdir -p roles molecule/universal reports docs; \ echo ""; \ echo "$(GREEN)✅ Инициализация проекта завершена!$(RESET)"; \ echo "$(GREEN)Созданы файлы:$(RESET)"; \ echo "$(GREEN)• .env - переменные окружения$(RESET)"; \ echo "$(GREEN)• vault/.vault - пароль vault$(RESET)"; \ echo "$(GREEN)• molecule/presets/minimal.yml - минимальный пресет$(RESET)"; \ echo ""; \ echo "$(YELLOW)Теперь вы можете использовать все возможности лаборатории!$(RESET)"; \ echo "$(YELLOW)Нажмите Enter для продолжения...$(RESET)"; \ read -r # ============================================================================= # ИНТЕРАКТИВНЫЕ МЕНЮ # ============================================================================= .PHONY: menu-lab menu-lab: check-whiptail ## Меню управления лабораторией @CHOICE=$$(whiptail --title "Управление лабораторией" \ --menu "Выберите действие:" 15 50 8 \ "up" "Запустить лабораторию" \ "down" "Остановить лабораторию" \ "test" "Тестировать лабораторию" \ "destroy" "Уничтожить лабораторию" \ "status" "Статус лаборатории" \ "logs" "Просмотр логов" \ "shell" "Подключиться к контейнеру" \ "back" "Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "up") \ echo "$(BLUE)🚀 Запуск лаборатории...$(RESET)"; \ if make lab up; then \ whiptail --title "Успех" --msgbox "Лаборатория успешно запущена!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при запуске лаборатории. Проверьте логи." 8 50; \ fi;; \ "down") \ echo "$(BLUE)🛑 Остановка лаборатории...$(RESET)"; \ if make lab down; then \ whiptail --title "Успех" --msgbox "Лаборатория успешно остановлена!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при остановке лаборатории." 8 50; \ fi;; \ "test") \ echo "$(BLUE)🧪 Тестирование лаборатории...$(RESET)"; \ if make lab test; then \ whiptail --title "Успех" --msgbox "Тесты лаборатории прошли успешно!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Тесты лаборатории не прошли. Проверьте логи." 8 50; \ fi;; \ "destroy") \ if whiptail --title "Подтверждение" --yesno "Вы уверены, что хотите уничтожить лабораторию? Это действие необратимо!" 8 50; then \ echo "$(BLUE)💥 Уничтожение лаборатории...$(RESET)"; \ if make lab destroy; then \ whiptail --title "Успех" --msgbox "Лаборатория успешно уничтожена!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при уничтожении лаборатории." 8 50; \ fi; \ fi;; \ "status") \ echo "$(BLUE)📊 Проверка статуса лаборатории...$(RESET)"; \ STATUS_OUTPUT=$$(make lab status 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$STATUS_OUTPUT" ]; then \ whiptail --title "Статус лаборатории" --msgbox "$$STATUS_OUTPUT" 15 70; \ else \ whiptail --title "Предупреждение" --msgbox "Лаборатория не запущена или статус недоступен." 8 50; \ fi;; \ "logs") \ echo "$(BLUE)📝 Получение логов лаборатории...$(RESET)"; \ LOGS_OUTPUT=$$(make lab logs 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$LOGS_OUTPUT" ]; then \ echo "$$LOGS_OUTPUT" | whiptail --title "Логи лаборатории" --scrolltext --textbox /dev/stdin 20 70; \ else \ whiptail --title "Предупреждение" --msgbox "Логи недоступны. Лаборатория может быть не запущена." 8 50; \ fi;; \ "shell") \ echo "$(BLUE)🐚 Подключение к контейнеру...$(RESET)"; \ whiptail --title "Shell" --msgbox "Подключение к контейнеру. Используйте Ctrl+D для выхода." 8 50; \ make lab shell;; \ esac .PHONY: menu-kube menu-kube: check-whiptail ## Меню управления Kubernetes @CHOICE=$$(whiptail --title "Управление Kubernetes" \ --menu "Выберите действие:" 15 50 8 \ "logs" "Просмотр логов" \ "exec" "Выполнить команду" \ "port-forward" "🔗 Проброс портов" \ "kiali" "Kiali Dashboard" \ "istio" "🌐 Istio Gateway" \ "grafana" "Grafana Dashboard" \ "prometheus" "Prometheus" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "logs") \ echo "$(BLUE)📝 Получение логов Kubernetes...$(RESET)"; \ KUBE_LOGS=$$(make kube logs 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$KUBE_LOGS" ]; then \ echo "$$KUBE_LOGS" | whiptail --title "Логи Kubernetes" --scrolltext --textbox /dev/stdin 20 70; \ else \ whiptail --title "Предупреждение" --msgbox "Логи Kubernetes недоступны. Кластер может быть не запущен." 8 50; \ fi;; \ "exec") \ COMMAND=$$(whiptail --inputbox "Введите команду для выполнения в кластере:" 8 50 "kubectl get pods" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$COMMAND" ]; then \ echo "$(BLUE)🐚 Выполнение команды: $$COMMAND$(RESET)"; \ EXEC_OUTPUT=$$(make kube exec CMD="$$COMMAND" 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$EXEC_OUTPUT" ]; then \ echo "$$EXEC_OUTPUT" | whiptail --title "Результат выполнения" --scrolltext --textbox /dev/stdin 20 70; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при выполнении команды в кластере." 8 50; \ fi; \ fi;; \ "port-forward") \ SERVICE=$$(whiptail --inputbox "Введите имя сервиса для проброса портов:" 8 50 "kiali" 3>&1 1>&2 2>&3); \ PORT=$$(whiptail --inputbox "Введите порт для проброса:" 8 50 "20001" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$SERVICE" ] && [ -n "$$PORT" ]; then \ echo "$(BLUE)🔗 Проброс портов для $$SERVICE:$$PORT$(RESET)"; \ whiptail --title "🔗 Port Forward" --msgbox "Проброс портов запущен. Используйте Ctrl+C для остановки." 8 50; \ make kube port-forward SERVICE="$$SERVICE" PORT="$$PORT"; \ fi;; \ "kiali") \ echo "$(BLUE)🔍 Запуск Kiali Dashboard...$(RESET)"; \ if make kube kiali; then \ whiptail --title "Успех" --msgbox "Kiali Dashboard доступен по адресу: http://localhost:20001" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при запуске Kiali Dashboard." 8 50; \ fi;; \ "istio") \ echo "$(BLUE)🌐 Запуск Istio Gateway...$(RESET)"; \ if make kube istio; then \ whiptail --title "Успех" --msgbox "Istio Gateway доступен по адресу: http://localhost:8080" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при запуске Istio Gateway." 8 50; \ fi;; \ "grafana") \ echo "$(BLUE)📊 Запуск Grafana Dashboard...$(RESET)"; \ if make kube grafana; then \ whiptail --title "Успех" --msgbox "Grafana Dashboard доступен по адресу: http://localhost:3000" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при запуске Grafana Dashboard." 8 50; \ fi;; \ "prometheus") \ echo "$(BLUE)📈 Запуск Prometheus...$(RESET)"; \ if make kube prometheus; then \ whiptail --title "Успех" --msgbox "Prometheus доступен по адресу: http://localhost:9090" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при запуске Prometheus." 8 50; \ fi;; \ esac .PHONY: menu-preset menu-preset: check-whiptail ## Меню управления пресетами @CHOICE=$$(whiptail --title "Управление пресетами" \ --menu "Выберите действие:" 15 50 7 \ "list" "Список пресетов" \ "create" "➕ Создать пресет" \ "edit" "✏️ Редактировать пресет" \ "test" "Тестировать пресет" \ "copy" "Копировать пресет" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "list") \ echo "$(BLUE)📋 Получение списка пресетов...$(RESET)"; \ PRESET_LIST=$$(make preset list 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$PRESET_LIST" ]; then \ echo "$$PRESET_LIST" | whiptail --title "Список пресетов" --scrolltext --textbox /dev/stdin 20 70; \ else \ whiptail --title "Предупреждение" --msgbox "Пресеты не найдены или недоступны." 8 50; \ fi;; \ "create") \ echo "$(CYAN)🎯 Создание нового пресета лаборатории$(RESET)"; \ PRESET_NAME=$$(whiptail --inputbox "Введите имя пресета (например: my-lab):" 8 50 "my-lab" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ PRESET_DESC=$$(whiptail --inputbox "Введите описание пресета:" 8 50 "Мой лабораторный пресет" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ HOST_COUNT=$$(whiptail --inputbox "Количество хостов:" 8 50 "3" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ OS_FAMILY=$$(whiptail --menu "Выберите семейство ОС:" 15 50 4 "debian" "Debian/Ubuntu" "redhat" "RHEL/CentOS" "mixed" "Смешанное" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ FEATURES=$$(whiptail --checklist "Выберите функции:" 15 50 6 "docker" "Docker" off "dind" "Docker-in-Docker" off "k8s" "Kubernetes" off "istio" "Istio" off "monitoring" "Мониторинг" off "chaos" "Chaos Engineering" off 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ echo "$(BLUE)📝 Создание пресета: $$PRESET_NAME$(RESET)"; \ mkdir -p molecule/presets; \ echo "---" > molecule/presets/$$PRESET_NAME.yml; \ echo "# Пресет: $$PRESET_DESC" >> molecule/presets/$$PRESET_NAME.yml; \ echo "# Автор: $(AUTHOR)" >> molecule/presets/$$PRESET_NAME.yml; \ echo "# Сайт: $(SITE)" >> molecule/presets/$$PRESET_NAME.yml; \ echo "" >> molecule/presets/$$PRESET_NAME.yml; \ echo "hosts:" >> molecule/presets/$$PRESET_NAME.yml; \ for i in $$(seq 1 $$HOST_COUNT); do \ echo " - name: host$$i" >> molecule/presets/$$PRESET_NAME.yml; \ if [ "$$OS_FAMILY" = "debian" ]; then \ echo " family: debian" >> molecule/presets/$$PRESET_NAME.yml; \ elif [ "$$OS_FAMILY" = "redhat" ]; then \ echo " family: redhat" >> molecule/presets/$$PRESET_NAME.yml; \ else \ if [ $$((i % 2)) -eq 0 ]; then \ echo " family: debian" >> molecule/presets/$$PRESET_NAME.yml; \ else \ echo " family: redhat" >> molecule/presets/$$PRESET_NAME.yml; \ fi; \ fi; \ echo " groups: [all]" >> molecule/presets/$$PRESET_NAME.yml; \ done; \ echo "" >> molecule/presets/$$PRESET_NAME.yml; \ echo "features:" >> molecule/presets/$$PRESET_NAME.yml; \ echo " docker: $$(echo $$FEATURES | grep -q docker && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " dind: $$(echo $$FEATURES | grep -q dind && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " k8s: $$(echo $$FEATURES | grep -q k8s && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " istio: $$(echo $$FEATURES | grep -q istio && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " monitoring: $$(echo $$FEATURES | grep -q monitoring && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " chaos: $$(echo $$FEATURES | grep -q chaos && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ whiptail --title "Успех" --msgbox "Пресет $$PRESET_NAME создан: molecule/presets/$$PRESET_NAME.yml\n\nИспользование: make lab up PRESET=$$PRESET_NAME" 10 70;; \ "edit") \ PRESET_NAME=$$(whiptail --inputbox "Введите имя пресета для редактирования:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$PRESET_NAME" ]; then \ echo "$(BLUE)✏️ Редактирование пресета: $$PRESET_NAME$(RESET)"; \ if make preset edit NAME="$$PRESET_NAME"; then \ whiptail --title "Успех" --msgbox "Пресет $$PRESET_NAME отредактирован!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при редактировании пресета $$PRESET_NAME." 8 50; \ fi; \ fi;; \ "test") \ PRESET_NAME=$$(whiptail --inputbox "Введите имя пресета для тестирования:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$PRESET_NAME" ]; then \ echo "$(BLUE)🧪 Тестирование пресета: $$PRESET_NAME$(RESET)"; \ if make preset test NAME="$$PRESET_NAME"; then \ whiptail --title "Успех" --msgbox "Пресет $$PRESET_NAME протестирован успешно!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при тестировании пресета $$PRESET_NAME." 8 50; \ fi; \ fi;; \ "copy") \ SOURCE_NAME=$$(whiptail --inputbox "Введите имя исходного пресета:" 8 50 "" 3>&1 1>&2 2>&3); \ TARGET_NAME=$$(whiptail --inputbox "Введите имя нового пресета:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$SOURCE_NAME" ] && [ -n "$$TARGET_NAME" ]; then \ echo "$(BLUE)📋 Копирование пресета: $$SOURCE_NAME -> $$TARGET_NAME$(RESET)"; \ if make preset copy SOURCE="$$SOURCE_NAME" TARGET="$$TARGET_NAME"; then \ whiptail --title "Успех" --msgbox "Пресет скопирован: $$SOURCE_NAME -> $$TARGET_NAME" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при копировании пресета." 8 50; \ fi; \ fi;; \ "interactive") \ echo "$(BLUE)🎯 Интерактивное создание пресета...$(RESET)"; \ if make preset-create-interactive; then \ whiptail --title "Успех" --msgbox "Пресет создан интерактивно!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при интерактивном создании пресета." 8 50; \ fi;; \ esac .PHONY: menu-role menu-role: check-whiptail ## Меню управления ролями @CHOICE=$$(whiptail --title "Управление ролями" \ --menu "Выберите действие:" 15 50 8 \ "list" "Список ролей" \ "create" "➕ Создать роль" \ "edit" "✏️ Редактировать роль" \ "test" "Тестировать роль" \ "lint" "Проверка синтаксиса" \ "deploy" "Развертывание ролей" \ "playbook" "Управление playbooks" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "list") \ echo "$(BLUE)📋 Получение списка ролей...$(RESET)"; \ ROLE_LIST=$$(make role list 2>&1); \ if [ $$? -eq 0 ] && [ -n "$$ROLE_LIST" ]; then \ echo "$$ROLE_LIST" | whiptail --title "Список ролей" --scrolltext --textbox /dev/stdin 20 70; \ else \ whiptail --title "Предупреждение" --msgbox "Роли не найдены или недоступны." 8 50; \ fi;; \ "create") \ echo "$(CYAN)🎯 Создание новой Ansible роли$(RESET)"; \ ROLE_NAME=$$(whiptail --inputbox "Введите имя роли (например: nginx):" 8 50 "nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ ROLE_DESC=$$(whiptail --inputbox "Введите описание роли:" 8 50 "Установка и настройка Nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ PACKAGE_NAME=$$(whiptail --inputbox "Имя пакета для установки:" 8 50 "nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ SERVICE_NAME=$$(whiptail --inputbox "Имя сервиса для управления:" 8 50 "nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ PLATFORMS=$$(whiptail --checklist "Поддерживаемые платформы:" 15 50 4 "debian" "Debian/Ubuntu" on "redhat" "RHEL/CentOS" on 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ TAGS=$$(whiptail --inputbox "Теги роли (через запятую):" 8 50 "web,nginx,http" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then exit 0; fi; \ echo "$(BLUE)📝 Создание роли: $$ROLE_NAME$(RESET)"; \ mkdir -p roles/$$ROLE_NAME/{tasks,handlers,templates,files,vars,defaults,meta}; \ echo "---" > roles/$$ROLE_NAME/meta/main.yml; \ echo "galaxy_info:" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " author: $(AUTHOR)" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " description: $$ROLE_DESC" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " company: $(SITE)" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " license: MIT" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " min_ansible_version: 2.9" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " platforms:" >> roles/$$ROLE_NAME/meta/main.yml; \ if echo "$$PLATFORMS" | grep -q debian; then \ echo " - name: Ubuntu" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " versions: [18.04, 20.04, 22.04]" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " - name: Debian" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " versions: [10, 11, 12]" >> roles/$$ROLE_NAME/meta/main.yml; \ fi; \ if echo "$$PLATFORMS" | grep -q redhat; then \ echo " - name: EL" >> roles/$$ROLE_NAME/meta/main.yml; \ echo " versions: [7, 8, 9]" >> roles/$$ROLE_NAME/meta/main.yml; \ fi; \ echo " galaxy_tags: [$$TAGS]" >> roles/$$ROLE_NAME/meta/main.yml; \ echo "" >> roles/$$ROLE_NAME/meta/main.yml; \ echo "dependencies: []" >> roles/$$ROLE_NAME/meta/main.yml; \ echo "---" > roles/$$ROLE_NAME/defaults/main.yml; \ echo "# Переменные по умолчанию для роли $$ROLE_NAME" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "# Автор: $(AUTHOR)" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "# Сайт: $(SITE)" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "$$ROLE_NAME_package: $$PACKAGE_NAME" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "$$ROLE_NAME_service: $$SERVICE_NAME" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "$$ROLE_NAME_enabled: true" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "$$ROLE_NAME_started: true" >> roles/$$ROLE_NAME/defaults/main.yml; \ echo "---" > roles/$$ROLE_NAME/tasks/main.yml; \ echo "# Основные задачи роли $$ROLE_NAME" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "# Автор: $(AUTHOR)" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "# Сайт: $(SITE)" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "- name: Включить задачи для Debian/Ubuntu" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo " import_tasks: debian.yml" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo " when: ansible_os_family == 'Debian'" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "- name: Включить задачи для RHEL/CentOS" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo " import_tasks: redhat.yml" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo " when: ansible_os_family == 'RedHat'" >> roles/$$ROLE_NAME/tasks/main.yml; \ echo "---" > roles/$$ROLE_NAME/tasks/debian.yml; \ echo "# Задачи для Debian/Ubuntu" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "- name: Обновить кэш пакетов" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " apt:" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " update_cache: yes" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " cache_valid_time: 3600" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "- name: Установить пакет $$PACKAGE_NAME" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " apt:" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " name: \"{{ $$ROLE_NAME_package }}\"" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " state: present" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "- name: Запустить и включить сервис $$SERVICE_NAME" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " systemd:" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " name: \"{{ $$ROLE_NAME_service }}\"" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " enabled: \"{{ $$ROLE_NAME_enabled }}\"" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo " state: \"{{ 'started' if $$ROLE_NAME_started else 'stopped' }}\"" >> roles/$$ROLE_NAME/tasks/debian.yml; \ echo "---" > roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "# Задачи для RHEL/CentOS" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "- name: Установить пакет $$PACKAGE_NAME" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " yum:" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " name: \"{{ $$ROLE_NAME_package }}\"" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " state: present" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "- name: Запустить и включить сервис $$SERVICE_NAME" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " systemd:" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " name: \"{{ $$ROLE_NAME_service }}\"" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " enabled: \"{{ $$ROLE_NAME_enabled }}\"" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo " state: \"{{ 'started' if $$ROLE_NAME_started else 'stopped' }}\"" >> roles/$$ROLE_NAME/tasks/redhat.yml; \ echo "---" > roles/$$ROLE_NAME/handlers/main.yml; \ echo "# Обработчики роли $$ROLE_NAME" >> roles/$$ROLE_NAME/handlers/main.yml; \ echo "" >> roles/$$ROLE_NAME/handlers/main.yml; \ echo "- name: Перезапустить $$SERVICE_NAME" >> roles/$$ROLE_NAME/handlers/main.yml; \ echo " systemd:" >> roles/$$ROLE_NAME/handlers/main.yml; \ echo " name: \"{{ $$ROLE_NAME_service }}\"" >> roles/$$ROLE_NAME/handlers/main.yml; \ echo " state: restarted" >> roles/$$ROLE_NAME/handlers/main.yml; \ whiptail --title "Успех" --msgbox "Роль $$ROLE_NAME создана в roles/$$ROLE_NAME/\n\nСтруктура:\n- tasks/ (основные задачи)\n- handlers/ (обработчики)\n- defaults/ (переменные)\n- meta/ (метаданные)\n\nИспользование: make role test NAME=$$ROLE_NAME" 15 70;; \ "edit") \ ROLE_NAME=$$(whiptail --inputbox "Введите имя роли для редактирования:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$ROLE_NAME" ]; then \ echo "$(BLUE)✏️ Редактирование роли: $$ROLE_NAME$(RESET)"; \ if make role edit NAME="$$ROLE_NAME"; then \ whiptail --title "Успех" --msgbox "Роль $$ROLE_NAME отредактирована!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при редактировании роли $$ROLE_NAME." 8 50; \ fi; \ fi;; \ "test") \ ROLE_NAME=$$(whiptail --inputbox "Введите имя роли для тестирования:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$ROLE_NAME" ]; then \ echo "$(BLUE)🧪 Тестирование роли: $$ROLE_NAME$(RESET)"; \ if make role test NAME="$$ROLE_NAME"; then \ whiptail --title "Успех" --msgbox "Роль $$ROLE_NAME протестирована успешно!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при тестировании роли $$ROLE_NAME." 8 50; \ fi; \ fi;; \ "lint") \ echo "$(BLUE)🔍 Проверка синтаксиса ролей...$(RESET)"; \ LINT_OUTPUT=$$(make role lint 2>&1); \ if [ $$? -eq 0 ]; then \ whiptail --title "Успех" --msgbox "Проверка синтаксиса ролей прошла успешно!" 8 50; \ else \ echo "$$LINT_OUTPUT" | whiptail --title "Ошибки синтаксиса" --scrolltext --textbox /dev/stdin 20 70; \ fi;; \ "deploy") \ echo "$(BLUE)🚀 Развертывание ролей...$(RESET)"; \ if make role deploy; then \ whiptail --title "Успех" --msgbox "Роли успешно развернуты!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при развертывании ролей." 8 50; \ fi;; \ "playbook") \ ROLE_NAME=$$(whiptail --inputbox "Введите имя роли для управления playbooks:" 8 50 "" 3>&1 1>&2 2>&3); \ if [ $$? -eq 0 ] && [ -n "$$ROLE_NAME" ]; then \ echo "$(BLUE)📝 Управление playbooks роли: $$ROLE_NAME$(RESET)"; \ if make role playbook NAME="$$ROLE_NAME"; then \ whiptail --title "Успех" --msgbox "Playbooks роли $$ROLE_NAME обработаны!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при обработке playbooks роли $$ROLE_NAME." 8 50; \ fi; \ fi;; \ "interactive") \ echo "$(BLUE)🎯 Интерактивное создание роли...$(RESET)"; \ if make role-create-interactive; then \ whiptail --title "Успех" --msgbox "Роль создана интерактивно!" 8 50; \ else \ whiptail --title "Ошибка" --msgbox "Ошибка при интерактивном создании роли." 8 50; \ fi;; \ esac .PHONY: menu-vault menu-vault: check-whiptail ## Меню управления vault @CHOICE=$$(whiptail --title "Управление vault" \ --menu "Выберите действие:" 15 50 8 \ "view" "👁️ Просмотр секретов" \ "create" "➕ Создать секрет" \ "edit" "✏️ Редактировать секрет" \ "encrypt" "Зашифровать файл" \ "decrypt" "🔓 Расшифровать файл" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "view") make vault view;; \ "create") make vault create;; \ "edit") make vault edit;; \ "encrypt") make vault encrypt;; \ "decrypt") make vault decrypt;; \ esac .PHONY: menu-report menu-report: check-whiptail ## Меню генерации отчетов @CHOICE=$$(whiptail --title "Генерация отчетов" \ --menu "Выберите действие:" 15 50 8 \ "html" "📄 HTML отчет" \ "json" "JSON отчет" \ "health" "🏥 Отчет о здоровье" \ "topology" "🗺️ Топология сети" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "html") make report;; \ "json") make report-json;; \ "health") make health-report;; \ "topology") make topology;; \ esac .PHONY: menu-lint menu-lint: check-whiptail ## Меню проверки синтаксиса @CHOICE=$$(whiptail --title "Проверка синтаксиса" \ --menu "Выберите действие:" 15 50 8 \ "all" "Проверить все" \ "roles" "Проверить роли" \ "playbooks" "Проверить playbooks" \ "vault" "Проверить vault" \ "secrets" "Проверить секреты" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "all") make lint;; \ "roles") make role lint;; \ "playbooks") make lint-playbooks;; \ "vault") make lint-vault;; \ "secrets") make check-secrets;; \ esac .PHONY: menu-snapshot menu-snapshot: check-whiptail ## Меню снимков лаборатории @CHOICE=$$(whiptail --title "Снимки лаборатории" \ --menu "Выберите действие:" 15 50 8 \ "create" "Создать снимок" \ "restore" "🔄 Восстановить снимок" \ "list" "Список снимков" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "create") make snapshot;; \ "restore") make restore;; \ "list") make snapshot-list;; \ esac .PHONY: menu-cleanup menu-cleanup: check-whiptail ## Меню очистки данных @CHOICE=$$(whiptail --title "Очистка данных" \ --menu "Выберите действие:" 15 50 8 \ "all" "Очистить все" \ "containers" "🐳 Очистить контейнеры" \ "images" "🖼️ Очистить образы" \ "volumes" "💾 Очистить тома" \ "reports" "Очистить отчеты" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "all") make cleanup;; \ "containers") make cleanup-containers;; \ "images") make cleanup-images;; \ "volumes") make cleanup-volumes;; \ "reports") make cleanup-reports;; \ esac .PHONY: menu-interactive menu-interactive: check-whiptail ## Меню интерактивных команд @CHOICE=$$(whiptail --title "Интерактивные команды" \ --menu "Выберите действие:" 15 50 8 \ "preset" "Создать пресет" \ "role" "Создать роль" \ "playbook" "Создать playbook" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "preset") make preset-create-interactive;; \ "role") make role-create-interactive;; \ "playbook") make playbook-create-interactive;; \ esac .PHONY: menu-docs menu-docs: check-whiptail ## Меню документации @CHOICE=$$(whiptail --title "Документация" \ --menu "Выберите действие:" 15 50 8 \ "readme" "📖 README" \ "lab" "Универсальная лаборатория" \ "presets" "Пресеты" \ "roles" "Роли" \ "examples" "💡 Примеры" \ "api" "🔧 API Reference" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "readme") cat README.md | less;; \ "lab") cat docs/universal-lab.md | less;; \ "presets") cat docs/presets.md | less;; \ "roles") cat docs/roles.md | less;; \ "examples") cat docs/examples.md | less;; \ "api") cat docs/api.md | less;; \ esac .PHONY: menu-help menu-help: check-whiptail ## Меню помощи и справки @CHOICE=$$(whiptail --title "Помощь и справка" \ --menu "Выберите действие:" 15 50 8 \ "overview" "Обзор проекта" \ "quickstart" "Быстрый старт" \ "commands" "⌨️ Список команд" \ "examples" "💡 Примеры использования" \ "troubleshooting" "🔧 Решение проблем" \ "about" "ℹ️ О проекте" \ "back" "⬅️ Назад" \ 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ] || [ "$$CHOICE" = "back" ]; then exit 0; fi; \ case "$$CHOICE" in \ "overview") \ whiptail --title "Обзор проекта" \ --msgbox "Ansible Template - Универсальная лаборатория для тестирования Ansible ролей и playbooks.\n\nОсновные возможности:\n• 🧪 Универсальная лаборатория с Docker\n• ☸️ Kubernetes кластеры (Kind)\n• 📋 Готовые пресеты для разных сценариев\n• 🎭 Управление Ansible ролями\n• 🔐 Безопасное хранение секретов\n• 📊 Генерация отчетов\n• 🎯 Интерактивные команды\n\nАвтор: $(AUTHOR)\nСайт: $(SITE)" 20 70;; \ "quickstart") \ whiptail --title "Быстрый старт" \ --msgbox "Быстрый старт:\n\n1. Инициализация проекта:\n make init\n\n2. Запуск лаборатории:\n make lab up\n\n3. Создание роли:\n make role-create-interactive\n\n4. Создание пресета:\n make preset-create-interactive\n\n5. Генерация отчета:\n make report\n\nВсе команды доступны через интерактивное меню!" 20 70;; \ "commands") \ whiptail --title "Список команд" \ --msgbox "Основные команды:\n\n• make - Главное меню\n• make help - Справка\n• make init - Инициализация\n• make lab up - Запуск лаборатории\n• make lab down - Остановка\n• make lab test - Тестирование\n• make role list - Список ролей\n• make preset list - Список пресетов\n• make vault view - Просмотр секретов\n• make report - Генерация отчета\n• make lint - Проверка синтаксиса\n• make cleanup - Очистка данных" 20 70;; \ "examples") \ whiptail --title "💡 Примеры использования" \ --msgbox "Примеры использования:\n\n1. Создание роли nginx:\n make role-create-interactive\n\n2. Создание пресета для 5 хостов:\n make preset-create-interactive\n\n3. Запуск лаборатории с пресетом:\n make lab up PRESET=my-preset\n\n4. Тестирование роли:\n make role test NAME=nginx\n\n5. Генерация HTML отчета:\n make report" 20 70;; \ "troubleshooting") \ whiptail --title "Решение проблем" \ --msgbox "Решение проблем:\n\n• whiptail не найден:\n make check-whiptail\n\n• Docker не запускается:\n make lab down && make lab up\n\n• Проблемы с vault:\n make vault view\n\n• Очистка всех данных:\n make cleanup\n\n• Проверка синтаксиса:\n make lint\n\n• Просмотр логов:\n make lab logs" 20 70;; \ "about") \ whiptail --title "О проекте" \ --msgbox "Ansible Template v$(VERSION)\n\nУниверсальная лаборатория для тестирования Ansible ролей и playbooks с поддержкой Docker, Kubernetes, и множества готовых пресетов.\n\nАвтор: $(AUTHOR)\nСайт: $(SITE)\nЛицензия: MIT\n\nОсобенности:\n• 🎯 Интерактивный интерфейс\n• 🧪 Универсальная лаборатория\n• ☸️ Kubernetes поддержка\n• 📋 21 готовый пресет\n• 🔐 Безопасность vault\n• 📊 Красивые отчеты" 20 70;; \ esac # ============================================================================= # ИНИЦИАЛИЗАЦИЯ И НАСТРОЙКА # ============================================================================= .PHONY: check-whiptail check-whiptail: ## Проверить и установить whiptail @if ! command -v whiptail >/dev/null 2>&1; then \ echo "$(YELLOW)🔧 whiptail не найден, устанавливаем...$(RESET)"; \ if command -v brew >/dev/null 2>&1; then \ echo "$(BLUE)🍺 Устанавливаем через Homebrew...$(RESET)"; \ brew install newt; \ elif command -v apt-get >/dev/null 2>&1; then \ echo "$(BLUE)📦 Устанавливаем через apt-get...$(RESET)"; \ sudo apt-get update && sudo apt-get install -y whiptail; \ elif command -v yum >/dev/null 2>&1; then \ echo "$(BLUE)📦 Устанавливаем через yum...$(RESET)"; \ sudo yum install -y newt; \ elif command -v dnf >/dev/null 2>&1; then \ echo "$(BLUE)📦 Устанавливаем через dnf...$(RESET)"; \ sudo dnf install -y newt; \ elif command -v pacman >/dev/null 2>&1; then \ echo "$(BLUE)📦 Устанавливаем через pacman...$(RESET)"; \ sudo pacman -S --noconfirm libnewt; \ elif command -v zypper >/dev/null 2>&1; then \ echo "$(BLUE)📦 Устанавливаем через zypper...$(RESET)"; \ sudo zypper install -y newt; \ else \ echo "$(RED)❌ Не удалось определить пакетный менеджер для установки whiptail$(RESET)"; \ echo "$(YELLOW)📖 Установите whiptail вручную для вашей ОС$(RESET)"; \ exit 1; \ fi; \ if command -v whiptail >/dev/null 2>&1; then \ echo "$(GREEN)✅ whiptail успешно установлен$(RESET)"; \ else \ echo "$(RED)❌ Ошибка установки whiptail$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(GREEN)✅ whiptail уже установлен$(RESET)"; \ fi .PHONY: init init: check-whiptail setup-env-interactive setup-vault setup-roles setup-precommit ## Полная инициализация проекта @echo "$(GREEN)✅ Проект успешно инициализирован!$(RESET)" @echo "$(YELLOW)📖 Документация: $(SITE)$(RESET)" @echo "$(YELLOW)🚀 Быстрый старт: make lab up$(RESET)" .PHONY: setup-env-interactive setup-env-interactive: ## Интерактивное создание .env файла @if [ ! -f $(ENV_FILE) ]; then \ echo "$(CYAN)🔧 Настройка проекта Ansible Template$(RESET)"; \ echo "$(YELLOW)📝 Заполните параметры проекта (можно оставить пустыми для значений по умолчанию):$(RESET)"; \ echo ""; \ read -p "$(YELLOW)📦 Название проекта [$(PROJECT_NAME)]: $(RESET)" USER_PROJECT_NAME; \ USER_PROJECT_NAME=$${USER_PROJECT_NAME:-$(PROJECT_NAME)}; \ read -p "$(YELLOW)📋 Версия проекта [$(VERSION)]: $(RESET)" USER_VERSION; \ USER_VERSION=$${USER_VERSION:-$(VERSION)}; \ read -p "$(YELLOW)👤 Автор проекта [Сергей Антропов]: $(RESET)" USER_AUTHOR; \ USER_AUTHOR=$${USER_AUTHOR:-Сергей Антропов}; \ read -p "$(YELLOW)🌐 Сайт проекта [https://devops.org.ru]: $(RESET)" USER_SITE; \ USER_SITE=$${USER_SITE:-https://devops.org.ru}; \ echo ""; \ echo "$(BLUE)🐳 Настройки Docker:$(RESET)"; \ read -p "$(YELLOW)🐳 Docker образ [$(DOCKER_IMAGE)]: $(RESET)" USER_DOCKER_IMAGE; \ USER_DOCKER_IMAGE=$${USER_DOCKER_IMAGE:-$(DOCKER_IMAGE)}; \ read -p "$(YELLOW)🌐 Docker сеть [$(DOCKER_NETWORK)]: $(RESET)" USER_DOCKER_NETWORK; \ USER_DOCKER_NETWORK=$${USER_DOCKER_NETWORK:-$(DOCKER_NETWORK)}; \ echo ""; \ echo "$(BLUE)🧪 Настройки лаборатории:$(RESET)"; \ read -p "$(YELLOW)📋 Сценарий Molecule [$(SCENARIO)]: $(RESET)" USER_SCENARIO; \ USER_SCENARIO=$${USER_SCENARIO:-$(SCENARIO)}; \ read -p "$(YELLOW)📄 Пресет лаборатории [$(LAB_SPEC)]: $(RESET)" USER_LAB_SPEC; \ USER_LAB_SPEC=$${USER_LAB_SPEC:-$(LAB_SPEC)}; \ echo ""; \ echo "$(BLUE)☸️ Настройки Kubernetes:$(RESET)"; \ read -p "$(YELLOW)☸️ Kubernetes контекст [$(KUBE_CONTEXT)]: $(RESET)" USER_KUBE_CONTEXT; \ USER_KUBE_CONTEXT=$${USER_KUBE_CONTEXT:-$(KUBE_CONTEXT)}; \ read -p "$(YELLOW)🔗 Версия Istio [$(ISTIO_VERSION)]: $(RESET)" USER_ISTIO_VERSION; \ USER_ISTIO_VERSION=$${USER_ISTIO_VERSION:-$(ISTIO_VERSION)}; \ read -p "$(YELLOW)☸️ Версия Kind [$(KIND_VERSION)]: $(RESET)" USER_KIND_VERSION; \ USER_KIND_VERSION=$${USER_KIND_VERSION:-$(KIND_VERSION)}; \ echo ""; \ echo "$(BLUE)📁 Настройки путей:$(RESET)"; \ read -p "$(YELLOW)📁 Папка ролей [$(ROLES_DIR)]: $(RESET)" USER_ROLES_DIR; \ USER_ROLES_DIR=$${USER_ROLES_DIR:-$(ROLES_DIR)}; \ read -p "$(YELLOW)🔐 Файл пароля vault [$(VAULT_PASSWORD_FILE)]: $(RESET)" USER_VAULT_PASSWORD_FILE; \ USER_VAULT_PASSWORD_FILE=$${USER_VAULT_PASSWORD_FILE:-$(VAULT_PASSWORD_FILE)}; \ echo ""; \ echo "$(BLUE)📝 Создание .env файла...$(RESET)"; \ echo "# Ansible Template Environment" > $(ENV_FILE); \ echo "# Автор: $(AUTHOR)" >> $(ENV_FILE); \ echo "# Сайт: $(SITE)" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Основные настройки" >> $(ENV_FILE); \ echo "PROJECT_NAME=$$USER_PROJECT_NAME" >> $(ENV_FILE); \ echo "VERSION=$$USER_VERSION" >> $(ENV_FILE); \ echo "AUTHOR=$$USER_AUTHOR" >> $(ENV_FILE); \ echo "SITE=$$USER_SITE" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Docker settings" >> $(ENV_FILE); \ echo "DOCKER_IMAGE=$$USER_DOCKER_IMAGE" >> $(ENV_FILE); \ echo "DOCKER_NETWORK=$$USER_DOCKER_NETWORK" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Molecule settings" >> $(ENV_FILE); \ echo "SCENARIO=$$USER_SCENARIO" >> $(ENV_FILE); \ echo "LAB_SPEC=$$USER_LAB_SPEC" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Kubernetes settings" >> $(ENV_FILE); \ echo "KUBE_CONTEXT=$$USER_KUBE_CONTEXT" >> $(ENV_FILE); \ echo "ISTIO_VERSION=$$USER_ISTIO_VERSION" >> $(ENV_FILE); \ echo "KIND_VERSION=$$USER_KIND_VERSION" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Paths" >> $(ENV_FILE); \ echo "ROLES_DIR=$$USER_ROLES_DIR" >> $(ENV_FILE); \ echo "VAULT_PASSWORD_FILE=$$USER_VAULT_PASSWORD_FILE" >> $(ENV_FILE); \ echo "$(GREEN)✅ .env файл создан с вашими настройками$(RESET)"; \ else \ echo "$(YELLOW)⚠️ .env файл уже существует$(RESET)"; \ echo "$(BLUE)📝 Текущие настройки:$(RESET)"; \ grep -v "^#" $(ENV_FILE) | grep -v "^$$" | head -10; \ fi .PHONY: setup-env setup-env: ## Создать .env файл с настройками по умолчанию @if [ ! -f $(ENV_FILE) ]; then \ echo "$(YELLOW)Создание .env файла...$(RESET)"; \ echo "# Ansible Template Environment" > $(ENV_FILE); \ echo "PROJECT_NAME=$(PROJECT_NAME)" >> $(ENV_FILE); \ echo "VERSION=$(VERSION)" >> $(ENV_FILE); \ echo "AUTHOR=$(AUTHOR)" >> $(ENV_FILE); \ echo "SITE=$(SITE)" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Docker settings" >> $(ENV_FILE); \ echo "DOCKER_IMAGE=$(DOCKER_IMAGE)" >> $(ENV_FILE); \ echo "DOCKER_NETWORK=$(DOCKER_NETWORK)" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Molecule settings" >> $(ENV_FILE); \ echo "SCENARIO=$(SCENARIO)" >> $(ENV_FILE); \ echo "LAB_SPEC=$(LAB_SPEC)" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Kubernetes settings" >> $(ENV_FILE); \ echo "KUBE_CONTEXT=$(KUBE_CONTEXT)" >> $(ENV_FILE); \ echo "ISTIO_VERSION=$(ISTIO_VERSION)" >> $(ENV_FILE); \ echo "KIND_VERSION=$(KIND_VERSION)" >> $(ENV_FILE); \ echo "" >> $(ENV_FILE); \ echo "# Paths" >> $(ENV_FILE); \ echo "ROLES_DIR=$(ROLES_DIR)" >> $(ENV_FILE); \ echo "VAULT_PASSWORD_FILE=$(VAULT_PASSWORD_FILE)" >> $(ENV_FILE); \ echo "$(GREEN)✅ .env файл создан$(RESET)"; \ else \ echo "$(YELLOW)⚠️ .env файл уже существует$(RESET)"; \ fi .PHONY: setup-vault setup-vault: ## Создать vault-password.txt @if [ ! -f $(VAULT_PASSWORD_FILE) ]; then \ echo "$(YELLOW)Создание vault-password.txt...$(RESET)"; \ echo "ansible-vault-password" > $(VAULT_PASSWORD_FILE); \ echo "$(GREEN)✅ vault-password.txt создан$(RESET)"; \ else \ echo "$(YELLOW)⚠️ vault-password.txt уже существует$(RESET)"; \ fi .PHONY: setup-roles setup-roles: ## Создать директорию для ролей @mkdir -p $(ROLES_DIR) @echo "$(GREEN)✅ Директория ролей создана: $(ROLES_DIR)$(RESET)" .PHONY: setup-precommit setup-precommit: ## Установить pre-commit хуки @if command -v pre-commit >/dev/null 2>&1; then \ pre-commit install; \ echo "$(GREEN)✅ Pre-commit хуки установлены$(RESET)"; \ else \ echo "$(YELLOW)⚠️ pre-commit не установлен. Установите: pip install pre-commit$(RESET)"; \ fi # ============================================================================= # УПРАВЛЕНИЕ ЛАБОРАТОРИЕЙ # ============================================================================= .PHONY: lab lab: ## Управление лабораторией (up|down|sh|test|create|converge|verify|destroy|reset) @case "$(word 2, $(MAKECMDGOALS))" in \ up) \ echo "$(GREEN)🚀 Поднимаем контроллер...$(RESET)"; \ $(DOCKER_COMPOSE) up -d; \ echo "$(GREEN)✅ Контроллер запущен$(RESET)";; \ down) \ echo "$(YELLOW)🛑 Останавливаем контроллер...$(RESET)"; \ $(DOCKER_COMPOSE) down -v; \ echo "$(GREEN)✅ Контроллер остановлен$(RESET)";; \ sh) \ echo "$(BLUE)🐚 Входим в контроллер...$(RESET)"; \ docker exec -it ansible-controller bash;; \ test) \ echo "$(PURPLE)🧪 Запускаем полный цикл тестирования...$(RESET)"; \ $(MAKE) lab up; \ docker exec -e MOLECULE_EPHEMERAL_DIRECTORY=$(MOLECULE_EPHEMERAL_DIRECTORY) ansible-controller \ bash -lc 'cd /ansible && molecule test -s $(SCENARIO)'; \ echo "$(GREEN)✅ Тестирование завершено$(RESET)";; \ create) \ echo "$(BLUE)🏗️ Создаем инфраструктуру...$(RESET)"; \ $(MAKE) lab up; \ docker exec -e MOLECULE_EPHEMERAL_DIRECTORY=$(MOLECULE_EPHEMERAL_DIRECTORY) ansible-controller \ bash -lc 'cd /ansible && molecule create -s $(SCENARIO)'; \ echo "$(GREEN)✅ Инфраструктура создана$(RESET)";; \ converge) \ echo "$(YELLOW)⚙️ Запускаем роли...$(RESET)"; \ docker exec -e MOLECULE_EPHEMERAL_DIRECTORY=$(MOLECULE_EPHEMERAL_DIRECTORY) ansible-controller \ bash -lc 'cd /ansible && molecule converge -s $(SCENARIO)'; \ echo "$(GREEN)✅ Роли выполнены$(RESET)";; \ verify) \ echo "$(CYAN)🔍 Проверяем работу лаборатории...$(RESET)"; \ docker exec -e MOLECULE_EPHEMERAL_DIRECTORY=$(MOLECULE_EPHEMERAL_DIRECTORY) ansible-controller \ bash -lc 'cd /ansible && molecule verify -s $(SCENARIO)'; \ echo "$(GREEN)✅ Проверка завершена$(RESET)";; \ destroy) \ echo "$(RED)💥 Уничтожаем инфраструктуру...$(RESET)"; \ docker exec -e MOLECULE_EPHEMERAL_DIRECTORY=$(MOLECULE_EPHEMERAL_DIRECTORY) ansible-controller \ bash -lc 'cd /ansible && molecule destroy -s $(SCENARIO)'; \ echo "$(GREEN)✅ Инфраструктура уничтожена$(RESET)";; \ reset) \ echo "$(PURPLE)🔄 Полный сброс лаборатории...$(RESET)"; \ $(MAKE) lab destroy; \ $(MAKE) lab down; \ $(MAKE) lab up; \ echo "$(GREEN)✅ Лаборатория сброшена$(RESET)";; \ *) \ echo "Неизвестная команда. Доступные: up, down, sh, test, create, converge, verify, destroy, reset";; \ esac # ============================================================================= # УПРАВЛЕНИЕ KUBERNETES # ============================================================================= .PHONY: kube kube: ## Управление Kubernetes (sh|cmd|enter|kiali|istio|grafana|prom|pf-stop|kubeconfig) @case "$(word 2, $(MAKECMDGOALS))" in \ sh) \ echo "$(BLUE)🐚 Входим в контейнер с kubectl...$(RESET)"; \ docker exec -it ansible-controller bash;; \ cmd) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube cmd CLUSTER=lab CMD=\"get pods -A\"$(RESET)"; \ exit 1; \ fi; \ echo "$(CYAN)🔧 Выполняем kubectl команду...$(RESET)"; \ docker exec -it ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) $(CMD)';; \ enter) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube enter CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)🚪 Входим в кластер...$(RESET)"; \ docker exec -it ansible-controller bash -lc '\ POD=$$(kubectl --context kind-$(CLUSTER) -n lab-demo get pod -l app=toolbox -o jsonpath="{.items[0].metadata.name}"); \ [ -n "$$POD" ] || { echo "toolbox pod not found"; exit 1; }; \ kubectl --context kind-$(CLUSTER) -n lab-demo exec -it $$POD -- /bin/sh';; \ kiali) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube kiali CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "$(PURPLE)🔮 Port-forward Kiali...$(RESET)"; \ docker exec -d ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) -n istio-system port-forward svc/kiali 20001:20001'; \ echo "$(GREEN)✅ Kiali: http://localhost:20001$(RESET)";; \ istio) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube istio CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "$(PURPLE)🌐 Port-forward Istio Gateway...$(RESET)"; \ docker exec -d ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) -n istio-system port-forward svc/istio-ingressgateway 8082:80 8444:443'; \ echo "$(GREEN)✅ Istio GW: http://localhost:8082 https://localhost:8444$(RESET)";; \ grafana) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube grafana CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)📊 Port-forward Grafana...$(RESET)"; \ docker exec -d ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) -n monitoring port-forward svc/monitoring-grafana 3000:80'; \ echo "$(GREEN)✅ Grafana: http://localhost:3000 (admin/admin)$(RESET)";; \ prom) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube prom CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)📈 Port-forward Prometheus...$(RESET)"; \ docker exec -d ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) -n monitoring port-forward svc/monitoring-kube-prometheus-prometheus 9090:9090'; \ echo "$(GREEN)✅ Prometheus: http://localhost:9090$(RESET)";; \ pf-stop) \ echo "$(RED)🛑 Останавливаем все port-forward...$(RESET)"; \ docker exec -it ansible-controller bash -lc 'pkill -f "kubectl .* port-forward" || true'; \ echo "$(GREEN)✅ Port-forward остановлены$(RESET)";; \ kubeconfig) \ if [ -z "$(CLUSTER)" ]; then \ echo "$(RED)❌ Использование: make kube kubeconfig CLUSTER=lab$(RESET)"; \ exit 1; \ fi; \ echo "Получаем kubeconfig для кластера $(CLUSTER)..."; \ mkdir -p reports/kubeconfigs; \ docker exec ansible-controller bash -lc 'kubectl --context kind-$(CLUSTER) config view --raw' > reports/kubeconfigs/kubeconfig-$(CLUSTER).yaml; \ echo "Kubeconfig сохранен: reports/kubeconfigs/kubeconfig-$(CLUSTER).yaml";; \ *) \ echo "Неизвестная команда. Доступные: sh, cmd, enter, kiali, istio, grafana, prom, pf-stop, kubeconfig";; \ esac # ============================================================================= # УПРАВЛЕНИЕ ПРЕСЕТАМИ # ============================================================================= .PHONY: preset-create-interactive preset-create-interactive: check-whiptail ## Интерактивное создание пресета @echo "$(CYAN)🎯 Создание нового пресета лаборатории$(RESET)"; \ PRESET_NAME=$$(whiptail --inputbox "Введите имя пресета (например: my-lab):" 8 50 "my-lab" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ PRESET_DESC=$$(whiptail --inputbox "Введите описание пресета:" 8 50 "Мой лабораторный пресет" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ HOST_COUNT=$$(whiptail --inputbox "Количество хостов:" 8 50 "3" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ OS_FAMILY=$$(whiptail --menu "Выберите семейство ОС:" 15 50 4 "debian" "Debian/Ubuntu" "redhat" "RHEL/CentOS" "mixed" "Смешанное" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ FEATURES=$$(whiptail --checklist "Выберите функции:" 15 50 6 "docker" "Docker" off "dind" "Docker-in-Docker" off "k8s" "Kubernetes" off "istio" "Istio" off "monitoring" "Мониторинг" off "chaos" "Chaos Engineering" off 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ echo "$(BLUE)📝 Создание пресета: $$PRESET_NAME$(RESET)"; \ mkdir -p molecule/presets; \ echo "---" > molecule/presets/$$PRESET_NAME.yml; \ echo "# Пресет: $$PRESET_DESC" >> molecule/presets/$$PRESET_NAME.yml; \ echo "# Автор: $(AUTHOR)" >> molecule/presets/$$PRESET_NAME.yml; \ echo "# Сайт: $(SITE)" >> molecule/presets/$$PRESET_NAME.yml; \ echo "" >> molecule/presets/$$PRESET_NAME.yml; \ echo "hosts:" >> molecule/presets/$$PRESET_NAME.yml; \ for i in $$(seq 1 $$HOST_COUNT); do \ echo " - name: host$$i" >> molecule/presets/$$PRESET_NAME.yml; \ if [ "$$OS_FAMILY" = "debian" ]; then \ echo " family: debian" >> molecule/presets/$$PRESET_NAME.yml; \ elif [ "$$OS_FAMILY" = "redhat" ]; then \ echo " family: redhat" >> molecule/presets/$$PRESET_NAME.yml; \ else \ if [ $$((i % 2)) -eq 0 ]; then \ echo " family: debian" >> molecule/presets/$$PRESET_NAME.yml; \ else \ echo " family: redhat" >> molecule/presets/$$PRESET_NAME.yml; \ fi; \ fi; \ echo " groups: [all]" >> molecule/presets/$$PRESET_NAME.yml; \ done; \ echo "" >> molecule/presets/$$PRESET_NAME.yml; \ echo "features:" >> molecule/presets/$$PRESET_NAME.yml; \ echo " docker: $$(echo $$FEATURES | grep -q docker && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " dind: $$(echo $$FEATURES | grep -q dind && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " k8s: $$(echo $$FEATURES | grep -q k8s && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " istio: $$(echo $$FEATURES | grep -q istio && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " monitoring: $$(echo $$FEATURES | grep -q monitoring && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo " chaos: $$(echo $$FEATURES | grep -q chaos && echo 'true' || echo 'false')" >> molecule/presets/$$PRESET_NAME.yml; \ echo "$(GREEN)✅ Пресет $$PRESET_NAME создан: molecule/presets/$$PRESET_NAME.yml$(RESET)"; \ echo "$(BLUE)📋 Использование: make lab up PRESET=$$PRESET_NAME$(RESET)" .PHONY: preset preset: ## Управление пресетами (list|create|edit|test|copy|delete) @case "$(word 2, $(MAKECMDGOALS))" in \ list) \ echo "Доступные пресеты:"; \ echo ""; \ echo "Классические пресеты:"; \ echo " minimal.yml - Минимальная лаборатория (1-3 машины)"; \ echo " webapp.yml - Веб-приложение (3-5 машин)"; \ echo " microservices.yml - Микросервисы (5-8 машин)"; \ echo " ha.yml - Высокая доступность (6-10 машин)"; \ echo " k8s-cluster.yml - Kubernetes кластер (8-12 машин)"; \ echo " cicd.yml - CI/CD пайплайн (10-15 машин)"; \ echo " bigdata.yml - Big Data кластер (12-18 машин)"; \ echo " servicemesh.yml - Service Mesh (15-20 машин)"; \ echo " enterprise.yml - Enterprise (18-20 машин)"; \ echo " maximum.yml - Максимальный (20 машин)"; \ echo ""; \ echo "Kubernetes пресеты:"; \ echo " k8s-single.yml - Kubernetes Single Node"; \ echo " k8s-multi.yml - Kubernetes Multi-Cluster"; \ echo " k8s-istio-full.yml - Kubernetes + Istio Full Stack"; \ echo ""; \ echo "DinD пресеты:"; \ echo " dind-simple.yml - DinD Simple"; \ echo " dind-swarm.yml - DinD Swarm"; \ echo " dind-compose.yml - DinD Compose"; \ echo ""; \ echo "DOoD пресеты:"; \ echo " dood-simple.yml - DOoD Simple"; \ echo " dood-mixed.yml - DOoD Mixed"; \ echo ""; \ echo "Смешанные пресеты:"; \ echo " mixed-k8s-dind.yml - Mixed Kubernetes + DinD"; \ echo " mixed-k8s-dood.yml - Mixed Kubernetes + DOoD"; \ echo " mixed-full.yml - Mixed Full Stack";; \ create) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make preset create NAME=my-preset$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)📝 Создаем пресет: $(NAME).yml$(RESET)"; \ $(MAKE) preset copy SOURCE=minimal.yml TARGET=$(NAME).yml; \ echo "$(GREEN)✅ Пресет создан: molecule/presets/$(NAME).yml$(RESET)";; \ edit) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make preset edit NAME=my-preset$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)✏️ Редактируем пресет: $(NAME).yml$(RESET)"; \ $${EDITOR:-vim} molecule/presets/$(NAME).yml;; \ test) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make preset test NAME=my-preset$(RESET)"; \ exit 1; \ fi; \ echo "$(PURPLE)🧪 Тестируем пресет: $(NAME).yml$(RESET)"; \ $(MAKE) lab test LAB_SPEC=molecule/presets/$(NAME).yml;; \ copy) \ if [ -z "$(SOURCE)" ] || [ -z "$(TARGET)" ]; then \ echo "$(RED)❌ Использование: make preset copy SOURCE=minimal.yml TARGET=my-preset.yml$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)📋 Копируем пресет: $(SOURCE) -> $(TARGET)$(RESET)"; \ cp molecule/presets/$(SOURCE) molecule/presets/$(TARGET); \ echo "$(GREEN)✅ Пресет скопирован$(RESET)";; \ delete) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make preset delete NAME=my-preset$(RESET)"; \ exit 1; \ fi; \ if [ -f "molecule/presets/$(NAME).yml" ]; then \ echo "$(RED)🗑️ Удаляем пресет: $(NAME)$(RESET)"; \ rm -f "molecule/presets/$(NAME).yml"; \ echo "$(GREEN)✅ Пресет $(NAME) удален$(RESET)"; \ else \ echo "$(RED)❌ Пресет $(NAME) не найден$(RESET)"; \ exit 1; \ fi;; \ *) \ echo "Неизвестная команда. Доступные: list, create, edit, test, copy, delete";; \ esac # ============================================================================= # УПРАВЛЕНИЕ РОЛЯМИ # ============================================================================= .PHONY: playbook-create-interactive playbook-create-interactive: check-whiptail ## Интерактивное создание playbook @echo "$(CYAN)🎯 Создание нового Ansible playbook$(RESET)"; \ PLAYBOOK_NAME=$$(whiptail --inputbox "Введите имя playbook (например: deploy.yml):" 8 50 "deploy.yml" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ PLAYBOOK_DESC=$$(whiptail --inputbox "Введите описание playbook:" 8 50 "Развертывание приложения" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ PLAYBOOK_HOSTS=$$(whiptail --inputbox "Целевые хосты (по умолчанию: all):" 8 50 "all" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ PLAYBOOK_ROLES=$$(whiptail --inputbox "Роли через запятую (например: nginx,apache):" 8 50 "nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ PLAYBOOK_TAGS=$$(whiptail --inputbox "Теги через запятую (например: deploy,web):" 8 50 "deploy" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ echo "$(BLUE)📝 Создание playbook: $$PLAYBOOK_NAME$(RESET)"; \ mkdir -p files/playbooks; \ echo "---" > files/playbooks/$$PLAYBOOK_NAME; \ echo "# Playbook: $$PLAYBOOK_DESC" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "# Автор: $(AUTHOR)" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "# Сайт: $(SITE)" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "- name: $$PLAYBOOK_DESC" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " hosts: $$PLAYBOOK_HOSTS" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " gather_facts: true" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " tags: [$$PLAYBOOK_TAGS]" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " pre_tasks:" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " - name: Update system packages" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " package:" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " name: '*'"; \ echo " state: latest" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " when: ansible_os_family in ['Debian', 'RedHat']" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " roles:" >> files/playbooks/$$PLAYBOOK_NAME; \ for role in $$(echo $$PLAYBOOK_ROLES | tr ',' ' '); do \ echo " - role: $$role" >> files/playbooks/$$PLAYBOOK_NAME; \ done; \ echo "" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " post_tasks:" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " - name: Verify deployment" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " debug:" >> files/playbooks/$$PLAYBOOK_NAME; \ echo " msg: \"Playbook $$PLAYBOOK_NAME completed successfully\"" >> files/playbooks/$$PLAYBOOK_NAME; \ echo "$(GREEN)✅ Playbook $$PLAYBOOK_NAME создан$(RESET)"; \ echo "$(BLUE)📁 Файл: files/playbooks/$$PLAYBOOK_NAME$(RESET)"; \ echo "$(BLUE)📋 Запуск: ansible-playbook files/playbooks/$$PLAYBOOK_NAME$(RESET)" .PHONY: role-create-interactive role-create-interactive: check-whiptail ## Интерактивное создание роли @echo "$(CYAN)🎯 Создание новой Ansible роли$(RESET)"; \ ROLE_NAME=$$(whiptail --inputbox "Введите имя роли (например: nginx):" 8 50 "nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ ROLE_DESC=$$(whiptail --inputbox "Введите описание роли:" 8 50 "Установка и настройка nginx" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ ROLE_PACKAGE=$$(whiptail --inputbox "Основной пакет (по умолчанию: $$ROLE_NAME):" 8 50 "$$ROLE_NAME" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ ROLE_SERVICE=$$(whiptail --inputbox "Имя сервиса (по умолчанию: $$ROLE_NAME):" 8 50 "$$ROLE_NAME" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ ROLE_PLATFORMS=$$(whiptail --checklist "Поддерживаемые платформы:" 15 50 4 "ubuntu" "Ubuntu" on "centos" "CentOS" on "rhel" "RHEL" off "debian" "Debian" off 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ ROLE_TAGS=$$(whiptail --inputbox "Теги через запятую (по умолчанию: $$ROLE_NAME):" 8 50 "$$ROLE_NAME,web,server" 3>&1 1>&2 2>&3); \ if [ $$? -ne 0 ]; then echo "$(RED)❌ Отменено$(RESET)"; exit 1; fi; \ echo "$(BLUE)📝 Создание роли: $$ROLE_NAME$(RESET)"; \ mkdir -p $(ROLES_DIR)/$${ROLE_NAME}/{tasks,handlers,templates,files,vars,defaults,meta,tests,playbooks}; \ echo "$(BLUE)📝 Создаем основные файлы...$(RESET)"; \ echo "---" > $(ROLES_DIR)/$${ROLE_NAME}/tasks/main.yml; \ echo "# Основные задачи роли $${ROLE_NAME}" >> $(ROLES_DIR)/$${ROLE_NAME}/tasks/main.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$${ROLE_NAME}/tasks/main.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$${ROLE_NAME}/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "- name: $$ROLE_NAME placeholder" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " debug:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " msg: \"Роль $$ROLE_NAME готова для настройки\"" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "- name: Include OS-specific tasks" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " include_tasks: \"{{ ansible_os_family | lower }}.yml\"" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "- name: Start $$ROLE_NAME service" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " systemd:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " name: \"{{ $${ROLE_NAME}_service | default('$$ROLE_SERVICE') }}\"" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " state: started" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " enabled: true" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo " when: $${ROLE_NAME}_service is defined" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml; \ echo "---" > $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "# Задачи для Debian/Ubuntu семейства" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "- name: Update apt cache (Debian)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " apt:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " update_cache: true" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " cache_valid_time: 3600" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " when: ansible_os_family == 'Debian'" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "- name: Install $$ROLE_NAME package (Debian)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " apt:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " name: \"{{ $${ROLE_NAME}_package | default('$$ROLE_PACKAGE') }}\"" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " state: present" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo " when: ansible_os_family == 'Debian'" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/debian.yml; \ echo "---" > $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "# Задачи для RHEL/CentOS семейства" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "- name: Update yum cache (RHEL)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " yum:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " update_cache: true" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " when: ansible_os_family == 'RedHat'" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "- name: Install $$ROLE_NAME package (RHEL)" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " yum:" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " name: \"{{ $${ROLE_NAME}_package | default('$$ROLE_PACKAGE') }}\"" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " state: present" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo " when: ansible_os_family == 'RedHat'" >> $(ROLES_DIR)/$$ROLE_NAME/tasks/redhat.yml; \ echo "---" > $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "# Переменные по умолчанию для роли $$ROLE_NAME" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "$${ROLE_NAME}_enabled: true" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "$${ROLE_NAME}_package: $$ROLE_PACKAGE" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "$${ROLE_NAME}_service: $$ROLE_SERVICE" >> $(ROLES_DIR)/$$ROLE_NAME/defaults/main.yml; \ echo "---" > $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo "galaxy_info:" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " author: $(AUTHOR)" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " description: $$ROLE_DESC" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " company: $(SITE)" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " license: MIT" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " min_ansible_version: 2.9" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " platforms:" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " - name: Ubuntu" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " versions: [focal, jammy]" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " - name: CentOS" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " versions: [7, 8, 9]" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " - name: RHEL" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " versions: [7, 8, 9]" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo " galaxy_tags: [$${ROLE_TAGS}]" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo "dependencies: []" >> $(ROLES_DIR)/$$ROLE_NAME/meta/main.yml; \ echo "---" > $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "# Роль $$ROLE_NAME" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "$$ROLE_DESC" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "## Переменные" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "| Переменная | По умолчанию | Описание |" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "|------------|--------------|----------|" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "| \`$${ROLE_NAME}_enabled\` | \`true\` | Включить роль |" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "| \`$${ROLE_NAME}_package\` | \`$$ROLE_PACKAGE\` | Имя пакета |" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "| \`$${ROLE_NAME}_service\` | \`$$ROLE_SERVICE\` | Имя сервиса |" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "## Использование" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "\`\`\`yaml" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "- hosts: all" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo " roles:" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo " - role: $$ROLE_NAME" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "\`\`\`" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "## Автор" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "$(AUTHOR)" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "Сайт: $(SITE)" >> $(ROLES_DIR)/$$ROLE_NAME/README.md; \ echo "$(GREEN)✅ Роль $$ROLE_NAME создана$(RESET)"; \ echo "$(BLUE)📁 Структура: $(ROLES_DIR)/$$ROLE_NAME/$(RESET)"; \ echo "$(BLUE)📝 Основной файл: $(ROLES_DIR)/$$ROLE_NAME/tasks/main.yml$(RESET)"; \ echo "$(BLUE)📋 Playbooks: $(ROLES_DIR)/$$ROLE_NAME/playbooks/$(RESET)" .PHONY: role role: ## Управление ролями (list|create|edit|test|lint|deploy|delete) @case "$(word 2, $(MAKECMDGOALS))" in \ list) \ echo "Доступные роли:"; \ if [ -d "$(ROLES_DIR)" ]; then \ ls -la $(ROLES_DIR)/ | grep "^d" | awk '{print " " $$9}' | grep -v "^\\.$$"; \ else \ echo " Директория ролей не найдена"; \ fi;; \ create) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role create NAME=my-role$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)📝 Создаем роль: $(NAME)$(RESET)"; \ echo "$(BLUE)🔧 Настройка роли...$(RESET)"; \ read -p "$(YELLOW)📝 Описание роли: $(RESET)" ROLE_DESC; \ read -p "$(YELLOW)📦 Основной пакет (по умолчанию: $(NAME)): $(RESET)" ROLE_PACKAGE; \ ROLE_PACKAGE=$${ROLE_PACKAGE:-$(NAME)}; \ read -p "$(YELLOW)🔧 Сервис (по умолчанию: $(NAME)): $(RESET)" ROLE_SERVICE; \ ROLE_SERVICE=$${ROLE_SERVICE:-$(NAME)}; \ read -p "$(YELLOW)📋 Платформы (ubuntu,centos,rhel) через запятую: $(RESET)" ROLE_PLATFORMS; \ ROLE_PLATFORMS=$${ROLE_PLATFORMS:-ubuntu,centos}; \ read -p "$(YELLOW)🏷️ Теги через запятую: $(RESET)" ROLE_TAGS; \ ROLE_TAGS=$${ROLE_TAGS:-$(NAME)}; \ echo "$(BLUE)📁 Создаем структуру роли...$(RESET)"; \ mkdir -p $(ROLES_DIR)/$(NAME)/{tasks,handlers,templates,files,vars,defaults,meta,tests,playbooks}; \ echo "$(BLUE)📝 Создаем основные файлы...$(RESET)"; \ echo "---" > $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "# Основные задачи роли $(NAME)" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "- name: $(NAME) placeholder" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " debug:" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " msg: \"Роль $(NAME) готова для настройки\"" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "- name: Include OS-specific tasks" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " include_tasks: \"{{ ansible_os_family | lower }}.yml\"" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "- name: Start $(NAME) service" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " systemd:" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " name: \"{{ $(NAME)_service | default('$(ROLE_SERVICE)') }}\"" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " state: started" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " enabled: true" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo " when: $(NAME)_service is defined" >> $(ROLES_DIR)/$(NAME)/tasks/main.yml; \ echo "---" > $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "# Задачи для Debian/Ubuntu семейства" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "- name: Update apt cache (Debian)" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " apt:" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " update_cache: true" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " cache_valid_time: 3600" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " when: ansible_os_family == 'Debian'" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "- name: Install $(NAME) package (Debian)" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " apt:" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " name: \"{{ $(NAME)_package | default('$(ROLE_PACKAGE)') }}\"" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " state: present" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo " when: ansible_os_family == 'Debian'" >> $(ROLES_DIR)/$(NAME)/tasks/debian.yml; \ echo "---" > $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "# Задачи для RHEL/CentOS семейства" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "- name: Update yum cache (RHEL)" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " yum:" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " update_cache: true" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " when: ansible_os_family == 'RedHat'" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "- name: Install $(NAME) package (RHEL)" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " yum:" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " name: \"{{ $(NAME)_package | default('$(ROLE_PACKAGE)') }}\"" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " state: present" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo " when: ansible_os_family == 'RedHat'" >> $(ROLES_DIR)/$(NAME)/tasks/redhat.yml; \ echo "---" > $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "# Переменные по умолчанию для роли $(NAME)" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "$(NAME)_enabled: true" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "$(NAME)_package: $(ROLE_PACKAGE)" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "$(NAME)_service: $(ROLE_SERVICE)" >> $(ROLES_DIR)/$(NAME)/defaults/main.yml; \ echo "---" > $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo "galaxy_info:" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " author: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " description: $(ROLE_DESC)" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " company: $(SITE)" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " license: MIT" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " min_ansible_version: 2.9" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " platforms:" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " - name: Ubuntu" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " versions: [focal, jammy]" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " - name: CentOS" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " versions: [7, 8, 9]" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " - name: RHEL" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " versions: [7, 8, 9]" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo " galaxy_tags: [$(ROLE_TAGS)]" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo "dependencies: []" >> $(ROLES_DIR)/$(NAME)/meta/main.yml; \ echo "---" > $(ROLES_DIR)/$(NAME)/README.md; \ echo "# Роль $(NAME)" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "$(ROLE_DESC)" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "## Переменные" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "| Переменная | По умолчанию | Описание |" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "|------------|--------------|----------|" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "| \`$(NAME)_enabled\` | \`true\` | Включить роль |" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "| \`$(NAME)_package\` | \`$(ROLE_PACKAGE)\` | Имя пакета |" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "| \`$(NAME)_service\` | \`$(ROLE_SERVICE)\` | Имя сервиса |" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "## Использование" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "\`\`\`yaml" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "- hosts: all" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo " roles:" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo " - role: $(NAME)" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "\`\`\`" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "## Автор" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "$(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/README.md; \ echo "$(GREEN)✅ Роль $(NAME) создана$(RESET)"; \ echo "$(BLUE)📁 Структура: $(ROLES_DIR)/$(NAME)/$(RESET)"; \ echo "$(BLUE)📝 Основной файл: $(ROLES_DIR)/$(NAME)/tasks/main.yml$(RESET)"; \ echo "$(BLUE)📋 Playbooks: $(ROLES_DIR)/$(NAME)/playbooks/$(RESET)";; \ edit) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role edit NAME=my-role$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)✏️ Редактируем роль: $(NAME)$(RESET)"; \ $${EDITOR:-vim} $(ROLES_DIR)/$(NAME)/tasks/main.yml;; \ test) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role test NAME=my-role$(RESET)"; \ exit 1; \ fi; \ echo "$(PURPLE)🧪 Тестируем роль: $(NAME)$(RESET)"; \ $(MAKE) lab test LAB_SPEC=molecule/presets/minimal.yml;; \ lint) \ echo "$(YELLOW)🔍 Проверяем роли...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-lint --config-file /ansible/.ansible-lint $(ROLES_DIR)/*'; \ echo "$(GREEN)✅ Проверка завершена$(RESET)";; \ deploy) \ echo "$(PURPLE)🚀 Развертываем роли...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-playbook -i /tmp/molecule/inventory/hosts.yml files/playbooks/site.yml'; \ echo "$(GREEN)✅ Развертывание завершено$(RESET)";; \ info) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role info NAME=my-role$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)📋 Информация о роли: $(NAME)$(RESET)"; \ docker exec ansible-controller bash -lc 'cat /ansible/roles/$(NAME)/README.md';; \ playbook) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role playbook NAME=my-role$(RESET)"; \ exit 1; \ fi; \ echo "$(BLUE)📋 Управление playbooks для роли: $(NAME)$(RESET)"; \ echo "$(YELLOW)Доступные команды:$(RESET)"; \ echo " $(CYAN)create$(RESET) - Создать новый playbook"; \ echo " $(CYAN)list$(RESET) - Список playbooks"; \ echo " $(CYAN)edit$(RESET) - Редактировать playbook"; \ echo " $(CYAN)run$(RESET) - Запустить playbook"; \ read -p "$(YELLOW)Выберите команду: $(RESET)" PLAYBOOK_CMD; \ case "$$PLAYBOOK_CMD" in \ create) \ read -p "$(YELLOW)📝 Имя playbook: $(RESET)" PLAYBOOK_NAME; \ if [ -z "$$PLAYBOOK_NAME" ]; then \ echo "$(RED)❌ Имя playbook обязательно$(RESET)"; \ exit 1; \ fi; \ echo "---" > $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "# Playbook: $$PLAYBOOK_NAME для роли $(NAME)" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "# Автор: $(AUTHOR)" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "# Сайт: $(SITE)" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "- name: $$PLAYBOOK_NAME" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " hosts: all" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " become: true" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " gather_facts: true" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " vars:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " # Переменные для роли $(NAME)" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " $(NAME)_enabled: true" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " pre_tasks:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " - name: Display OS information" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " debug:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " msg: \"OS Family: {{ ansible_os_family }}, OS: {{ ansible_distribution }} {{ ansible_distribution_version }}\"" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " roles:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " - role: $(NAME)" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " post_tasks:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " - name: Verify $(NAME) installation" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " debug:" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo " msg: \"$(NAME) successfully deployed on {{ inventory_hostname }}\"" >> $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ echo "$(GREEN)✅ Playbook $$PLAYBOOK_NAME создан$(RESET)";; \ list) \ echo "$(CYAN)📋 Playbooks для роли $(NAME):$(RESET)"; \ if [ -d "$(ROLES_DIR)/$(NAME)/playbooks" ]; then \ ls -la $(ROLES_DIR)/$(NAME)/playbooks/*.yml 2>/dev/null | awk '{print " " $$9}' || echo " $(YELLOW)Нет playbooks$(RESET)"; \ else \ echo " $(YELLOW)Директория playbooks не найдена$(RESET)"; \ fi;; \ edit) \ read -p "$(YELLOW)📝 Имя playbook для редактирования: $(RESET)" PLAYBOOK_NAME; \ if [ -f "$(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml" ]; then \ $${EDITOR:-vim} $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml; \ else \ echo "$(RED)❌ Playbook $$PLAYBOOK_NAME не найден$(RESET)"; \ fi;; \ run) \ read -p "$(YELLOW)📝 Имя playbook для запуска: $(RESET)" PLAYBOOK_NAME; \ if [ -f "$(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml" ]; then \ echo "$(BLUE)🚀 Запускаем playbook: $$PLAYBOOK_NAME$(RESET)"; \ docker exec ansible-controller bash -lc "ansible-playbook -i /tmp/molecule/inventory/hosts.yml $(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml"; \ else \ echo "$(RED)❌ Playbook $$PLAYBOOK_NAME не найден$(RESET)"; \ fi;; \ delete) \ read -p "$(YELLOW)📝 Имя playbook для удаления: $(RESET)" PLAYBOOK_NAME; \ if [ -f "$(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml" ]; then \ echo "$(RED)🗑️ Удаляем playbook: $$PLAYBOOK_NAME$(RESET)"; \ rm -f "$(ROLES_DIR)/$(NAME)/playbooks/$$PLAYBOOK_NAME.yml"; \ echo "$(GREEN)✅ Playbook $$PLAYBOOK_NAME удален$(RESET)"; \ else \ echo "$(RED)❌ Playbook $$PLAYBOOK_NAME не найден$(RESET)"; \ fi;; \ *) \ echo "Неизвестная команда playbook. Доступные: create, list, edit, run, delete";; \ esac;; \ delete) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make role delete NAME=my-role$(RESET)"; \ exit 1; \ fi; \ if [ -d "$(ROLES_DIR)/$(NAME)" ]; then \ echo "$(RED)🗑️ Удаляем роль: $(NAME)$(RESET)"; \ rm -rf "$(ROLES_DIR)/$(NAME)"; \ echo "$(GREEN)✅ Роль $(NAME) удалена$(RESET)"; \ else \ echo "$(RED)❌ Роль $(NAME) не найдена$(RESET)"; \ exit 1; \ fi;; \ *) \ echo "Неизвестная команда. Доступные: list, create, edit, test, lint, deploy, delete, info, playbook";; \ esac # ============================================================================= # УПРАВЛЕНИЕ VAULT # ============================================================================= .PHONY: vault vault: ## Управление Ansible Vault (show|create|edit|delete|rekey|decrypt|encrypt) @case "$(word 2, $(MAKECMDGOALS))" in \ show) \ echo "Показываем содержимое vault..."; \ docker exec ansible-controller bash -lc 'ansible-vault view --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ create) \ echo "$(YELLOW)📝 Создаем vault файл...$(RESET)"; \ docker exec ansible-controller bash -lc 'echo "---" > vault/secrets.yml && ansible-vault encrypt --encrypt-vault-id default --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ edit) \ echo "$(BLUE)✏️ Редактируем vault файл...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-vault edit --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ delete) \ echo "$(RED)🗑️ Удаляем vault файл...$(RESET)"; \ docker exec ansible-controller bash -lc 'rm vault/secrets.yml';; \ rekey) \ echo "$(YELLOW)🔑 Изменяем пароль vault...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-vault rekey --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ decrypt) \ echo "$(GREEN)🔓 Расшифровываем vault файл...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-vault decrypt --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ encrypt) \ echo "$(RED)🔒 Шифруем vault файл...$(RESET)"; \ docker exec ansible-controller bash -lc 'ansible-vault encrypt --encrypt-vault-id default --vault-password-file $(VAULT_PASSWORD_FILE) vault/secrets.yml';; \ *) \ echo "Неизвестная команда. Доступные: show, create, edit, delete, rekey, decrypt, encrypt";; \ esac # ============================================================================= # УПРАВЛЕНИЕ GIT # ============================================================================= .PHONY: git git: ## Управление Git (status|add|commit|push|pull|branch|merge) @case "$(word 2, $(MAKECMDGOALS))" in \ status) \ echo "$(CYAN)📊 Статус Git репозитория:$(RESET)"; \ git status;; \ add) \ echo "$(GREEN)➕ Добавляем файлы в Git...$(RESET)"; \ git add .;; \ commit) \ if [ -z "$(MESSAGE)" ]; then \ echo "$(RED)❌ Использование: make git commit MESSAGE=\"your commit message\"$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)💾 Создаем коммит...$(RESET)"; \ git commit -m "$(MESSAGE)";; \ push) \ echo "$(BLUE)🚀 Отправляем изменения...$(RESET)"; \ git push;; \ pull) \ echo "$(GREEN)📥 Получаем изменения...$(RESET)"; \ git pull;; \ branch) \ if [ -z "$(NAME)" ]; then \ echo "$(RED)❌ Использование: make git branch NAME=my-branch$(RESET)"; \ exit 1; \ fi; \ echo "$(PURPLE)🌿 Создаем ветку: $(NAME)$(RESET)"; \ git checkout -b $(NAME);; \ merge) \ if [ -z "$(BRANCH)" ]; then \ echo "$(RED)❌ Использование: make git merge BRANCH=my-branch$(RESET)"; \ exit 1; \ fi; \ echo "$(YELLOW)🔀 Сливаем ветку: $(BRANCH)$(RESET)"; \ git merge $(BRANCH);; \ *) \ echo "Неизвестная команда. Доступные: status, add, commit, push, pull, branch, merge";; \ esac # ============================================================================= # УПРАВЛЕНИЕ DOCKER # ============================================================================= .PHONY: docker docker: ## Управление Docker (build|rebuild|prune|shell|logs|stop|start) @case "$(word 2, $(MAKECMDGOALS))" in \ build) \ echo "$(YELLOW)🔨 Собираем Docker образы...$(RESET)"; \ $(DOCKER_COMPOSE) build;; \ rebuild) \ echo "$(YELLOW)🔨 Пересобираем Docker образы...$(RESET)"; \ $(DOCKER_COMPOSE) build --no-cache;; \ prune) \ echo "$(RED)🧹 Очищаем Docker...$(RESET)"; \ docker system prune -af;; \ shell) \ echo "$(BLUE)🐚 Входим в Docker контейнер...$(RESET)"; \ docker exec -it ansible-controller bash;; \ logs) \ echo "$(CYAN)📋 Показываем логи...$(RESET)"; \ $(DOCKER_COMPOSE) logs -f;; \ stop) \ echo "$(RED)🛑 Останавливаем контейнеры...$(RESET)"; \ $(DOCKER_COMPOSE) stop;; \ start) \ echo "$(GREEN)▶️ Запускаем контейнеры...$(RESET)"; \ $(DOCKER_COMPOSE) start;; \ *) \ echo "Неизвестная команда. Доступные: build, rebuild, prune, shell, logs, stop, start";; \ esac # ============================================================================= # ОТЧЕТЫ И МОНИТОРИНГ # ============================================================================= .PHONY: report report: ## Сгенерировать HTML отчет @echo "$(PURPLE)📊 Генерируем HTML отчет...$(RESET)" @mkdir -p reports @docker exec ansible-controller bash -lc 'python3 /ansible/scripts/report_html.py /ansible/reports/lab-health.json /ansible/reports/lab-report.html' @echo "$(GREEN)✅ HTML отчет: reports/lab-report.html$(RESET)" @echo "$(YELLOW)📖 Откройте отчет в браузере:$(RESET)" @echo " $(BLUE)file://$(PWD)/reports/lab-report.html$(RESET)" .PHONY: kubeconfigs kubeconfigs: ## Получить все kubeconfig файлы @echo "$(BLUE)📋 Получаем все kubeconfig файлы...$(RESET)" @mkdir -p reports/kubeconfigs @docker exec ansible-controller bash -lc 'for cluster in $$(kind get clusters 2>/dev/null || echo ""); do \ if [ -n "$$cluster" ]; then \ echo "Получаем kubeconfig для $$cluster..."; \ kubectl --context kind-$$cluster config view --raw > /ansible/reports/kubeconfigs/kubeconfig-$$cluster.yaml; \ fi; \ done' @echo "$(GREEN)✅ Kubeconfig файлы сохранены в reports/kubeconfigs/$(RESET)" @if [ -d "reports/kubeconfigs" ] && [ -n "$$(ls reports/kubeconfigs/ 2>/dev/null)" ]; then \ echo "$(YELLOW)📁 Найденные kubeconfig файлы:$(RESET)"; \ ls -la reports/kubeconfigs/ | grep -v "^total" | awk '{print " " $$9}'; \ fi .PHONY: open-report open-report: ## Открыть HTML отчет в браузере @if [ -f "reports/lab-report.html" ]; then \ echo "$(BLUE)🌐 Открываем отчет в браузере...$(RESET)"; \ if command -v open >/dev/null 2>&1; then \ open reports/lab-report.html; \ elif command -v xdg-open >/dev/null 2>&1; then \ xdg-open reports/lab-report.html; \ else \ echo "$(YELLOW)⚠️ Откройте отчет вручную: file://$(PWD)/reports/lab-report.html$(RESET)"; \ fi; \ echo "$(GREEN)✅ Отчет открыт$(RESET)"; \ else \ echo "$(RED)❌ Отчет не найден. Сначала выполните: make report$(RESET)"; \ fi .PHONY: full-test full-test: ## Полный цикл тестирования с отчетом и kubeconfig @echo "$(PURPLE)🚀 Запускаем полный цикл тестирования...$(RESET)" @$(MAKE) lab test @echo "$(BLUE)📊 Генерируем отчеты...$(RESET)" @$(MAKE) report @$(MAKE) kubeconfigs @echo "$(GREEN)✅ Полный цикл завершен!$(RESET)" @echo "$(YELLOW)📁 Результаты:$(RESET)" @echo " $(BLUE)📊 HTML отчет: reports/lab-report.html$(RESET)" @echo " $(BLUE)📋 Kubeconfig файлы: reports/kubeconfigs/$(RESET)" @echo "$(YELLOW)🌐 Открыть отчет: make open-report$(RESET)" .PHONY: chaos chaos: ## Запустить Chaos Engineering тесты @echo "$(RED)🧨 Запускаем Chaos Engineering...$(RESET)" @docker exec ansible-controller bash -lc 'ansible-playbook -i /tmp/molecule/inventory/hosts.yml /ansible/files/playbooks/chaos.yml' @echo "$(GREEN)✅ Chaos Engineering завершен$(RESET)" .PHONY: check-secrets check-secrets: ## Проверить безопасность секретов @echo "$(YELLOW)🔍 Проверяем безопасность секретов...$(RESET)" @docker exec ansible-controller bash -lc 'bash /ansible/scripts/secret_scan.sh' @echo "$(GREEN)✅ Проверка секретов завершена$(RESET)" .PHONY: idempotence idempotence: ## Проверить идемпотентность @echo "$(BLUE)🔄 Проверяем идемпотентность...$(RESET)" @docker exec ansible-controller bash -lc 'ansible-playbook -i /tmp/molecule/inventory/hosts.yml /ansible/files/playbooks/site.yml --check' @echo "$(GREEN)✅ Идемпотентность проверена$(RESET)" .PHONY: snapshot snapshot: ## Сохранить снапшот лаборатории @echo "$(YELLOW)📸 Создаем снапшот...$(RESET)" @docker exec ansible-controller bash -lc 'bash /ansible/scripts/snapshot.sh' @echo "$(GREEN)✅ Снапшот сохранен$(RESET)" .PHONY: restore restore: ## Восстановить из снапшота @echo "$(BLUE)🔄 Восстанавливаем из снапшота...$(RESET)" @docker exec ansible-controller bash -lc 'bash /ansible/scripts/restore.sh' @echo "$(GREEN)✅ Снапшот восстановлен$(RESET)" .PHONY: cleanup cleanup: ## Очистить лабораторию @echo "$(RED)🧹 Очищаем лабораторию...$(RESET)" @docker exec ansible-controller bash -lc 'bash /ansible/scripts/cleanup.sh' @echo "$(GREEN)✅ Лаборатория очищена$(RESET)" # ============================================================================= # УТИЛИТЫ # ============================================================================= .PHONY: lint lint: ## Проверить весь проект на ошибки @echo "Проверяем весь проект..." @docker exec ansible-controller bash -lc 'ansible-lint --config-file /ansible/.ansible-lint molecule/universal/' @docker exec ansible-controller bash -lc 'ansible-lint --config-file /ansible/.ansible-lint files/playbooks/' @if [ -d "$(ROLES_DIR)" ] && [ -n "$$(ls $(ROLES_DIR)/ 2>/dev/null)" ]; then \ docker exec ansible-controller bash -lc 'ansible-lint --config-file /ansible/.ansible-lint $(ROLES_DIR)/*'; \ fi @echo "Проверка завершена" .PHONY: env env: ## Показать переменные окружения @echo "$(CYAN)🔧 Переменные окружения:$(RESET)" @echo "PROJECT_NAME: $(PROJECT_NAME)" @echo "VERSION: $(VERSION)" @echo "AUTHOR: $(AUTHOR)" @echo "SITE: $(SITE)" @echo "DOCKER_IMAGE: $(DOCKER_IMAGE)" @echo "DOCKER_NETWORK: $(DOCKER_NETWORK)" @echo "SCENARIO: $(SCENARIO)" @echo "LAB_SPEC: $(LAB_SPEC)" @echo "KUBE_CONTEXT: $(KUBE_CONTEXT)" @echo "ROLES_DIR: $(ROLES_DIR)" @echo "VAULT_PASSWORD_FILE: $(VAULT_PASSWORD_FILE)" .PHONY: clean clean: cleanup ## Полная очистка проекта @echo "$(RED)🧹 Полная очистка проекта...$(RESET)" @rm -rf .env @rm -rf vault/ @rm -rf reports/ @rm -rf snapshots/ @echo "$(GREEN)✅ Проект очищен$(RESET)" # ============================================================================= # ЗАГЛУШКИ ДЛЯ ПАРАМЕТРОВ # ============================================================================= %: @: # ============================================================================= # ЗАГРУЗКА ПЕРЕМЕННЫХ ИЗ .env # ============================================================================= ifneq (,$(wildcard .env)) include .env export endif