# ============================================================================= # AnsibleLab - Универсальная система тестирования Ansible ролей # Автор: Сергей Антропов # Сайт: https://devops.org.ru # ============================================================================= SHELL := /bin/bash # ============================================================================= # ПЕРЕМЕННЫЕ # ============================================================================= # Цвета для вывода 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 # Глобальные переменные PROJECT_NAME ?= ansible-lab VERSION ?= 0.1.0 AUTHOR ?= "Сергей Антропов" SITE ?= "https://devops.org.ru" DOCKER_IMAGE ?= inecs/ansible-lab:ansible-controller-latest DOCKER_K8S_IMAGE ?= inecs/ansible-lab:k8s-latest DOCKER_DIND_IMAGE ?= docker:27-dind CONTAINER_NAME ?= ansible-controller # Переменные для Docker Hub DOCKER_REGISTRY ?= inecs/ansible-lab DOCKER_VERSION ?= latest DOCKER_IMAGES := ansible-controller alt-linux astra-linux redos rhel centos7 centos8 centos9 alma rocky ubuntu20 ubuntu22 ubuntu24 debian9 debian10 debian11 debian12 # Multi-arch поддержка DOCKER_PLATFORMS ?= linux/amd64,linux/arm64 DOCKER_BUILDX_BUILDER ?= multiarch-builder # Базовые образы и их теги BASE_IMAGES := altlinux/p9 astralinux/astra-1.7 redos/redos:9 registry.access.redhat.com/ubi8/ubi centos:7 quay.io/centos/centos:8 quay.io/centos/centos:stream9 almalinux:8 rockylinux:8 ubuntu:20.04 ubuntu:22.04 ubuntu:24.04 debian:9 debian:10 debian:11 debian:bookworm .PHONY: role vault git docker presets controller k8s help update-playbooks generate-docs setup-cicd list create delete # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С РОЛЯМИ # ============================================================================= role: @case "$(word 2, $(MAKECMDGOALS))" in \ lint) \ ROLE_NAME="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$ROLE_NAME" ]; then \ echo "🔍 Проверка синтаксиса всех ролей ..."; \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/ --config-file .ansible-lint || true"; \ else \ echo "🔍 Проверка синтаксиса роли: $$ROLE_NAME"; \ if [ -d "roles/$$ROLE_NAME" ]; then \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/$$ROLE_NAME/ --config-file .ansible-lint || true"; \ else \ echo "❌ Роль '$$ROLE_NAME' не найдена в roles/"; \ echo "📋 Доступные роли:"; \ ls -1 roles/ | grep -v "\.yml$$" | sed 's/^/ - /'; \ exit 1; \ fi; \ fi; \ echo ""; \ echo "✅ Lint завершен";; \ test) \ echo "🚀 Тестирование ролей ..."; \ PRESET="default"; \ ARGS="$(wordlist 3,10,$(MAKECMDGOALS))"; \ if [ -n "$$ARGS" ]; then \ PRESET="$$(echo $$ARGS | cut -d' ' -f1)"; \ fi; \ echo "📋 Используется пресет: $$PRESET"; \ if [ ! -f "molecule/presets/$$PRESET.yml" ]; then \ echo "❌ Ошибка: Пресет '$$PRESET' не найден!"; \ echo "💡 Доступные пресеты:"; \ ls -1 molecule/presets/*.yml 2>/dev/null | sed 's|molecule/presets/||g' | sed 's|\.yml||g' | sed 's/^/ - /' || echo " ⚠️ Пресеты не найдены"; \ exit 1; \ fi; \ echo ""; \ echo "🔧 Запуск ansible-controller контейнера..."; \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v /var/run/docker.sock:/var/run/docker.sock \ -u root \ -e ANSIBLE_FORCE_COLOR=1 \ -e MOLECULE_PRESET=$$PRESET \ -e MOLECULE_EPHEMERAL_DIRECTORY=/tmp/molecule_workspace \ $(DOCKER_IMAGE) \ bash -c "mkdir -p /tmp/molecule_workspace/inventory && cd molecule/default && ansible-playbook -i localhost, create.yml --connection=local -e molecule_ephemeral_directory=/tmp/molecule_workspace && ansible-playbook -i /tmp/molecule_workspace/inventory/hosts.ini site.yml; ansible-playbook -i localhost, destroy.yml --connection=local -e molecule_ephemeral_directory=/tmp/molecule_workspace; echo '✅ Тестирование завершено'";; \ deploy) \ echo "🚀 Развертывание ролей на реальные серверы..."; \ echo ""; \ if [ ! -f "inventory/hosts.ini" ]; then \ echo "❌ Ошибка: Файл inventory/hosts.ini не найден!"; \ echo "💡 Создайте файл inventory/hosts.ini с вашими серверами"; \ exit 1; \ fi; \ echo "📋 Используется inventory: inventory/hosts.ini"; \ echo "📄 Содержимое inventory:"; \ docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) cat inventory/hosts.ini || cat inventory/hosts.ini; \ echo ""; \ echo "🚀 Запуск развертывания (в контейнере)..."; \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v ~/.ssh:/root/.ssh:ro \ -e ANSIBLE_FORCE_COLOR=1 \ $(DOCKER_IMAGE) \ bash -c "ansible-playbook -i inventory/hosts.ini roles/deploy.yml --check"; \ echo ""; \ read -p "Продолжить развертывание? (y/N): " confirm; \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v ~/.ssh:/root/.ssh:ro \ -e ANSIBLE_FORCE_COLOR=1 \ $(DOCKER_IMAGE) \ bash -c "ansible-playbook -i inventory/hosts.ini roles/deploy.yml"; \ else \ echo "❌ Развертывание отменено"; \ fi;; \ list) \ ./scripts/role-manager.sh list;; \ create) \ ./scripts/role-manager.sh create < /dev/tty;; \ delete) \ ./scripts/role-manager.sh delete < /dev/tty;; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🚀 make role test [preset] - протестировать роли с preset'ом"; \ echo " 💡 Примеры:"; \ echo " make role test # с default preset"; \ echo " make role test minimal # с minimal preset"; \ echo " make role test all-images # со всеми образами"; \ echo " make role test etcd-patroni # с etcd-patroni preset"; \ echo ""; \ echo " 🚀 make role deploy - развернуть роли на реальные серверы"; \ echo " 💡 Требует: inventory/hosts.ini"; \ echo " 💡 Примеры:"; \ echo " make role deploy # развертывание всех ролей"; \ echo " ansible-playbook -i inventory/hosts.ini roles/deploy.yml --tags web"; \ echo " ansible-playbook -i inventory/hosts.ini roles/deploy.yml --limit webservers"; \ echo " ansible-playbook -i inventory/hosts.ini roles/deploy.yml --check"; \ echo " 💡 Документация: docs/deploy-yml-customization.md"; \ echo ""; \ echo " 🔍 make role lint [role] - проверить синтаксис ролей"; \ echo " 💡 Использует: ansible-lint"; \ echo " 💡 Без параметра: проверяет все роли"; \ echo " 💡 С параметром: проверяет конкретную роль"; \ echo " 💡 Валидация: показывает доступные роли при ошибке"; \ echo " 💡 Примеры:"; \ echo " make role lint # проверить все роли"; \ echo " make role lint devops # проверить только devops"; \ echo " make role lint ping # проверить только ping"; \ echo ""; \ echo " 📋 make role list - показать все роли"; \ echo " 💡 Показывает: список всех ролей в roles/"; \ echo ""; \ echo " ➕ make role create - создать новую роль"; \ echo " 💡 Интерактивно: запрашивает имя роли"; \ echo " 💡 Создает: структуру папок и файлов"; \ echo " 💡 Обновляет: roles/deploy.yml"; \ echo ""; \ echo " 🗑️ make role delete - удалить роль"; \ echo " 💡 Интерактивно: запрашивает имя роли"; \ echo " 💡 Удаляет: папку роли и файлы"; \ echo " 💡 Обновляет: roles/deploy.yml"; \ echo ""; \ echo " 🔧 ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О ЛИНТИНГЕ:"; \ echo " 💡 Линтинг проверяет: синтаксис, стиль, лучшие практики"; \ echo " 💡 Профили: production, basic, min"; \ echo " 💡 Конфигурация: .ansible-lint"; \ echo " 💡 Ошибки: показываются с номерами строк"; \ echo " 💡 Валидация: автоматическая проверка существования роли";; \ esac # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С PRESET'АМИ # ============================================================================= presets: @case "$(word 2, $(MAKECMDGOALS))" in \ list) \ echo "📋 Доступные пресеты:"; \ echo ""; \ preset_count=0; \ for preset in molecule/presets/*.yml; do \ if [ -f "$$preset" ]; then \ preset_name=$$(basename "$$preset" .yml); \ preset_desc=$$(grep -E "^#description:" "$$preset" | head -1 | sed 's/^#description: *//' || echo "Описание отсутствует"); \ host_count=$$(grep -c "^- name:" "$$preset" 2>/dev/null || echo "?"); \ printf " 📄 %s - %s (%s хостов)\n" "$$preset_name" "$$preset_desc" "$$host_count"; \ preset_count=$$((preset_count + 1)); \ fi; \ done; \ if [ $$preset_count -eq 0 ]; then \ echo " ⚠️ Пресеты не найдены"; \ fi;; \ info) \ if [ -z "$(PRESET)" ]; then \ echo "❌ Ошибка: Укажите PRESET=имя_пресета"; \ echo "💡 Пример: make presets info PRESET=etcd-patroni"; \ exit 1; \ fi; \ if [ ! -f "molecule/presets/$(PRESET).yml" ]; then \ echo "❌ Ошибка: Пресет '$(PRESET)' не найден!"; \ echo "💡 Доступные пресеты:"; \ make presets list; \ exit 1; \ fi; \ echo "📋 Информация о пресете: $(PRESET)"; \ echo ""; \ echo "📄 Описание:"; \ grep -E "^#description:" "molecule/presets/$(PRESET).yml" | head -1 | sed 's/^#description: *//' || echo "Описание отсутствует"; \ echo ""; \ echo "🏠 Хосты:"; \ grep -E "^- name:" "molecule/presets/$(PRESET).yml" | sed 's/^- name: / - /' || echo "Хосты не найдены"; \ echo ""; \ echo "🌐 Сеть:"; \ grep -E "^docker_network:" "molecule/presets/$(PRESET).yml" | sed 's/^docker_network: / - /' || echo "Сеть не указана"; \ echo ""; \ echo "🐳 Образы:"; \ grep -E "^- " "molecule/presets/$(PRESET).yml" | grep -E "family:" | sed 's/.*family: / - /' || echo "Образы не найдены";; \ test) \ if [ -z "$(PRESET)" ]; then \ echo "❌ Ошибка: Укажите PRESET=имя_пресета"; \ echo "💡 Пример: make presets test PRESET=etcd-patroni"; \ exit 1; \ fi; \ if [ ! -f "molecule/presets/$(PRESET).yml" ]; then \ echo "❌ Ошибка: Пресет '$(PRESET)' не найден!"; \ echo "💡 Доступные пресеты:"; \ make presets list; \ exit 1; \ fi; \ echo "🚀 Тестирование с пресетом: $(PRESET)"; \ echo ""; \ docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v /var/run/docker.sock:/var/run/docker.sock \ -e ANSIBLE_FORCE_COLOR=1 \ -e MOLECULE_PRESET=$(PRESET) \ $(DOCKER_IMAGE) \ bash -c "cd molecule/default && molecule test" || echo "✅ Тестирование завершено";; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 📋 make presets list - показать список всех preset'ов"; \ echo " 💡 Показывает: название, описание, количество хостов"; \ echo ""; \ echo " 📄 make presets info - подробная информация о preset'е"; \ echo " 💡 Показывает: описание, хосты, сеть, образы"; \ echo " 💡 Требует: PRESET=имя_пресета"; \ echo ""; \ echo " 🚀 make presets test - запустить тест с preset'ом"; \ echo " 💡 Запускает: molecule test с выбранным preset'ом"; \ echo " 💡 Требует: PRESET=имя_пресета"; \ echo ""; \ echo "💡 Примеры:"; \ echo " make presets list # показать все preset'ы"; \ echo " make presets info PRESET=etcd-patroni # информация о etcd-patroni"; \ echo " make presets test PRESET=minimal # тест с minimal preset"; \ echo " make presets test PRESET=performance # тест с performance preset";; \ esac # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С VAULT # ============================================================================= vault: @case "$(word 2, $(MAKECMDGOALS))" in \ init) \ echo "🔐 Инициализация vault..."; \ if [ ! -f "vault/.vault" ]; then \ echo "📝 Создание файла vault/.vault..."; \ read -sp "Введите пароль для vault: " PASSWORD; \ echo ""; \ echo "$$PASSWORD" > vault/.vault; \ chmod 600 vault/.vault; \ echo "✅ Файл vault/.vault создан"; \ else \ echo "✅ Файл vault/.vault уже существует"; \ fi;; \ create) \ if [ ! -f "vault/.vault" ]; then \ echo "⚠️ Файл vault/.vault не найден!"; \ echo "💡 Сначала создайте файл: make vault init"; \ exit 1; \ fi; \ echo "🔐 Создание файла секретов..."; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -it -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault create --encrypt-vault-id default --vault-password-file vault/.vault vault/$$FILE.yml;; \ edit) \ echo "🔐 Редактирование секретов..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет зашифрованных файлов"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -it -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault edit --vault-password-file vault/.vault vault/$$FILE.yml;; \ show) \ echo "🔐 Просмотр секретов..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет зашифрованных файлов"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault view --vault-password-file vault/.vault vault/$$FILE.yml;; \ delete) \ echo "🔐 Удаление секретов..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет зашифрованных файлов"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ rm -f vault/$$FILE.yml;; \ encrypt) \ echo "🔐 Шифрование файла..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет файлов для шифрования"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault encrypt --encrypt-vault-id default --vault-password-file vault/.vault vault/$$FILE.yml;; \ decrypt) \ echo "🔐 Расшифровка файла..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет зашифрованных файлов"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault decrypt --vault-password-file vault/.vault vault/$$FILE.yml;; \ rekey) \ echo "🔐 Смена пароля..."; \ ls -la vault/*.yml 2>/dev/null || echo "Нет зашифрованных файлов"; \ echo ""; \ read -p "Введите имя файла (без .yml): " FILE; \ docker run --rm -it -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) \ ansible-vault rekey --vault-password-file vault/.vault vault/$$FILE.yml;; \ check) \ echo "🔍 Проверка vault файлов..."; \ if [ ! -d "vault" ]; then \ echo "❌ Директория vault не найдена"; \ exit 1; \ fi; \ vault_files=$$(find vault -name "*.yml" -type f 2>/dev/null); \ if [ -z "$$vault_files" ]; then \ echo "⚠️ Vault файлы не найдены"; \ exit 0; \ fi; \ echo "📋 Найденные vault файлы:"; \ for file in $$vault_files; do \ echo " 📄 $$file"; \ done; \ echo ""; \ echo "🔍 Проверка структуры..."; \ for file in $$vault_files; do \ if grep -q "ANSIBLE_VAULT" "$$file"; then \ echo " ✅ $$file - зашифрован"; \ else \ echo " ⚠️ $$file - не зашифрован"; \ fi; \ done;; \ scan) \ echo "🔍 Поиск секретов в проекте..."; \ echo "📋 Поиск потенциальных секретов:"; \ find . -name "*.yml" -o -name "*.yaml" | grep -v ".git" | while read file; do \ if grep -qE "(password|secret|key|token|api_key)" "$$file" 2>/dev/null; then \ echo " ⚠️ $$file - содержит потенциальные секреты"; \ fi; \ done; \ echo ""; \ echo "💡 Рекомендации:"; \ echo " - Используйте ansible-vault для шифрования секретов"; \ echo " - Не храните секреты в открытом виде"; \ echo " - Регулярно проверяйте файлы на наличие секретов";; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🔑 make vault init - инициализировать vault (создать vault/.vault)"; \ echo " 💡 Первая команда для настройки vault"; \ echo ""; \ echo " 🔐 make vault create - создать новый файл секретов"; \ echo " 💡 Интерактивное создание зашифрованного файла"; \ echo ""; \ echo " ✏️ make vault edit - редактировать существующие секреты"; \ echo " 💡 Открывает редактор для изменения секретов"; \ echo ""; \ echo " 👁️ make vault show - показать содержимое секретов"; \ echo " 💡 Расшифровывает и показывает содержимое"; \ echo ""; \ echo " 🗑️ make vault delete - удалить файл секретов"; \ echo " 💡 Безвозвратное удаление файла"; \ echo ""; \ echo " 🔒 make vault encrypt - зашифровать существующий файл"; \ echo " 💡 Шифрует незашифрованный файл"; \ echo ""; \ echo " 🔓 make vault decrypt - расшифровать файл"; \ echo " 💡 Создает незашифрованную копию"; \ echo ""; \ echo " 🔑 make vault rekey - сменить пароль шифрования"; \ echo " 💡 Изменяет пароль для существующего файла"; \ echo ""; \ echo " ✅ make vault check - проверить vault файлы"; \ echo " 💡 Проверяет структуру и статус файлов"; \ echo ""; \ echo " 🔍 make vault scan - поиск потенциальных секретов"; \ echo " 💡 Сканирует проект на наличие незашифрованных секретов";; \ esac # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С GIT # ============================================================================= git: @case "$(word 2, $(MAKECMDGOALS))" in \ push) \ echo "📤 Отправка изменений в репозиторий..."; \ git add .; \ git commit -m "Обновление проекта"; \ git push origin main;; \ pull) \ echo "📥 Получение изменений из репозитория..."; \ git pull origin main;; \ new) \ echo "🌿 Создание новой ветки..."; \ read -p "Введите имя ветки: " BRANCH; \ git checkout -b "$$BRANCH"; \ echo "✅ Ветка '$$BRANCH' создана";; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 📤 make git push - отправить изменения в репозиторий"; \ echo " 💡 Выполняет: git add . && git commit && git push"; \ echo ""; \ echo " 📥 make git pull - получить изменения из репозитория"; \ echo " 💡 Выполняет: git pull origin main"; \ echo ""; \ echo " 🌿 make git new - создать новую ветку"; \ echo " 💡 Интерактивно запрашивает имя ветки"; \ echo " 💡 Выполняет: git checkout -b имя_ветки";; \ esac # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С DOCKER # ============================================================================= docker: @case "$(word 2, $(MAKECMDGOALS))" in \ prepare) \ echo "🔧 Подготовка Docker образов для Docker Hub..."; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "📋 Version: $(DOCKER_VERSION)"; \ echo "📋 Images: $(DOCKER_IMAGES)"; \ echo ""; \ echo "💡 Для работы с Docker Hub выполните:"; \ echo " docker login - авторизация в Docker Hub"; \ echo " make docker build - сборка образов"; \ echo " make docker push - отправка в Docker Hub";; \ build) \ echo "🐳 Сборка Docker образов (multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "📋 Version: $(DOCKER_VERSION)"; \ echo "📋 Images: $(DOCKER_IMAGES)"; \ echo "⚠️ ВНИМАНИЕ: RED OS и Astra Linux собираются только для AMD64"; \ echo ""; \ $(MAKE) docker setup-builder; \ for image in $(DOCKER_IMAGES); do \ echo "🔨 Сборка $(DOCKER_REGISTRY)/$$image:$(DOCKER_VERSION)"; \ $(MAKE) docker-build-image IMAGE=$$image; \ done; \ echo "✅ Образы собраны";; \ rebuild) \ echo "🔄 Полная пересборка Docker образов (multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "📋 Version: $(DOCKER_VERSION)"; \ echo "📋 Images: $(DOCKER_IMAGES)"; \ echo "⚠️ ВНИМАНИЕ: RED OS и Astra Linux собираются только для AMD64"; \ echo "🧹 Очистка кеша и старых образов..."; \ echo ""; \ $(MAKE) docker clean; \ $(MAKE) docker clean-builder; \ $(MAKE) docker setup-builder; \ for image in $(DOCKER_IMAGES); do \ echo "🔨 Пересборка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ $(MAKE) docker-build-image IMAGE=$$image; \ done; \ echo "✅ Образы пересобраны с нуля";; \ push) \ echo "📤 Отправка Docker образов в Docker Hub..."; \ for image in $(DOCKER_IMAGES); do \ echo "📤 Отправка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ docker push $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION); \ done; \ echo "✅ Образы отправлены в Docker Hub";; \ pull) \ echo "📥 Загрузка Docker образов из Docker Hub..."; \ for image in $(DOCKER_IMAGES); do \ echo "📥 Загрузка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ docker pull $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) || echo "⚠️ Образ $$image не найден в Docker Hub"; \ done; \ echo "✅ Загрузка завершена";; \ clean) \ echo "🧹 Очистка Docker образов и builds..."; \ for image in $(DOCKER_IMAGES); do \ echo "🗑️ Удаление $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ docker rmi $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) 2>/dev/null || true; \ done; \ echo "🗑️ Удаление кеша builds для наших образов..."; \ docker buildx prune --filter type=exec.cachemount --filter type=source.local --filter type=source.git.checkout --force 2>/dev/null || true; \ echo "✅ Образы и кеш builds очищены";; \ info) \ echo "📊 Информация об образах..."; \ for image in $(DOCKER_IMAGES); do \ if docker images | grep -q "$(DOCKER_REGISTRY):$$image"; then \ echo "📦 $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ docker images | grep "$(DOCKER_REGISTRY):$$image" | head -1; \ fi; \ done;; \ update) \ echo "🔄 Обновление всех образов..."; \ $(MAKE) docker pull; \ $(MAKE) docker build; \ $(MAKE) docker push; \ echo "✅ Все образы обновлены";; \ purge) \ echo "🧹 Полная очистка Docker..."; \ echo "⚠️ ВНИМАНИЕ: Это удалит ВСЕ Docker данные!"; \ echo ""; \ read -p "Продолжить? (y/N): " confirm; \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ echo "🛑 Остановка всех контейнеров..."; \ docker stop $$(docker ps -aq) 2>/dev/null || true; \ echo "🗑️ Удаление всех контейнеров..."; \ docker rm $$(docker ps -aq) 2>/dev/null || true; \ echo "🗑️ Удаление всех образов..."; \ docker rmi $$(docker images -aq) 2>/dev/null || true; \ echo "🗑️ Удаление всех томов..."; \ docker volume rm $$(docker volume ls -q) 2>/dev/null || true; \ echo "🗑️ Удаление всех сетей..."; \ docker network rm $$(docker network ls -q) 2>/dev/null || true; \ echo "🧹 Очистка системы..."; \ docker system prune -af --volumes; \ echo "✅ Docker полностью очищен"; \ else \ echo "❌ Очистка отменена"; \ fi;; \ clean-builder) \ echo "🧹 Очистка multi-arch builder..."; \ $(MAKE) docker-reset-builder;; \ build-image) \ if [ -z "$(IMAGE)" ]; then \ echo "❌ Ошибка: Укажите IMAGE=имя_образа"; \ echo "💡 Пример: make docker build-image IMAGE=centos"; \ echo "💡 Доступные образы: $(DOCKER_IMAGES)"; \ exit 1; \ fi; \ if [ ! -d "dockerfiles/$(IMAGE)" ]; then \ echo "❌ Ошибка: Директория dockerfiles/$(IMAGE) не найдена!"; \ echo "💡 Доступные образы:"; \ for img in $(DOCKER_IMAGES); do \ if [ -d "dockerfiles/$$img" ]; then \ echo " - $$img"; \ fi; \ done; \ exit 1; \ fi; \ echo "🔨 Сборка отдельного образа: $(IMAGE)"; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo ""; \ $(MAKE) docker setup-builder; \ $(MAKE) docker-build-image IMAGE=$(IMAGE); \ echo "✅ Образ $(IMAGE) собран";; \ setup-builder) \ echo "🔧 Настройка multi-arch builder в контейнере..."; \ if $(MAKE) docker-check-builder >/dev/null 2>&1; then \ echo "✅ Builder $(DOCKER_BUILDX_BUILDER) уже существует и готов"; \ docker buildx use $(DOCKER_BUILDX_BUILDER); \ else \ echo "📦 Создание builder $(DOCKER_BUILDX_BUILDER)..."; \ $(MAKE) docker-create-builder; \ fi; \ echo "🔍 Финальная проверка builder..."; \ $(MAKE) docker-check-builder;; \ diagnose) \ echo "🔍 Диагностика buildx проблем..."; \ $(MAKE) docker-diagnose-buildx;; \ reset-builder) \ echo "🔄 Сброс buildx builder..."; \ $(MAKE) docker-reset-builder;; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🔧 make docker prepare - подготовка к работе с Docker Hub"; \ echo " 💡 Показывает: registry, version, список образов"; \ echo " 💡 Рекомендует: docker login перед работой"; \ echo ""; \ echo " 🐳 make docker build - собрать все Docker образы (multi-arch)"; \ echo " 💡 Собирает: ansible-controller, alt-linux, astra-linux, redos"; \ echo " 💡 Собирает: rhel, centos, alma, rocky, ubuntu, debian"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo " 💡 Ограничения: RED OS и Astra Linux только AMD64"; \ echo " 💡 Тегирует: inecs/образ: (автоматически извлекает теги)"; \ echo " 💡 Отправляет: автоматически в Docker Hub"; \ echo ""; \ echo " 🔨 make docker build-image IMAGE=<имя> - собрать отдельный образ"; \ echo " 💡 Пример: make docker build-image IMAGE=centos"; \ echo " 💡 Собирает: только указанный образ (multi-arch)"; \ echo " 💡 Доступные образы: $(DOCKER_IMAGES)"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo ""; \ echo " 🔄 make docker rebuild - полная пересборка с очисткой кеша"; \ echo " 💡 Очищает: все локальные образы и кеш"; \ echo " 💡 Пересобирает: все образы с нуля"; \ echo " 💡 Полезно: при проблемах с кешем или зависимостями"; \ echo " 💡 Выполняет: clean + clean-builder + setup-builder + build"; \ echo ""; \ echo " 📤 make docker push - отправить образы в Docker Hub"; \ echo " 💡 Требует: docker login"; \ echo " 💡 Отправляет: все образы в registry inecs"; \ echo ""; \ echo " 📥 make docker pull - загрузить образы из Docker Hub"; \ echo " 💡 Загружает: все образы из registry inecs"; \ echo " 💡 Пропускает: отсутствующие образы"; \ echo ""; \ echo " 🧹 make docker clean - удалить локальные образы и кеш builds"; \ echo " 💡 Удаляет: все образы inecs/ansible-lab:*"; \ echo " 💡 Очищает: кеш builds (exec.cachemount, source.local, git.checkout)"; \ echo " 💡 Сохраняет: другие builds в системе"; \ echo " 💡 Безопасно: игнорирует ошибки"; \ echo ""; \ echo " 🧹 make docker clean-builder - очистка multi-arch builder"; \ echo " 💡 Удаляет: builder контейнер и buildkit контейнеры"; \ echo " 💡 Полезно: при проблемах со сборкой";; \ setup-builder) \ echo "🔧 Настройка multi-arch builder в контейнере..."; \ if $(MAKE) docker-check-builder >/dev/null 2>&1; then \ echo "✅ Builder $(DOCKER_BUILDX_BUILDER) уже существует и готов"; \ docker buildx use $(DOCKER_BUILDX_BUILDER); \ else \ echo "📦 Создание builder $(DOCKER_BUILDX_BUILDER)..."; \ $(MAKE) docker-create-builder; \ fi; \ echo "🔍 Финальная проверка builder..."; \ $(MAKE) docker-check-builder;; \ diagnose) \ echo "🔍 Диагностика buildx проблем..."; \ $(MAKE) docker-diagnose-buildx;; \ reset-builder) \ echo "🔄 Сброс buildx builder..."; \ $(MAKE) docker-reset-builder;; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🔧 make docker prepare - подготовка к работе с Docker Hub"; \ echo " 💡 Показывает: registry, version, список образов"; \ echo " 💡 Рекомендует: docker login перед работой"; \ echo ""; \ echo " 🐳 make docker build - собрать все Docker образы (multi-arch)"; \ echo " 💡 Собирает: ansible-controller, alt-linux, astra-linux, redos"; \ echo " 💡 Собирает: rhel, centos, alma, rocky, ubuntu, debian"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo " 💡 Ограничения: RED OS и Astra Linux только AMD64"; \ echo " 💡 Тегирует: inecs/образ: (автоматически извлекает теги)"; \ echo " 💡 Отправляет: автоматически в Docker Hub"; \ echo ""; \ echo " 🔨 make docker build-image IMAGE=<имя> - собрать отдельный образ"; \ echo " 💡 Пример: make docker build-image IMAGE=centos"; \ echo " 💡 Собирает: только указанный образ (multi-arch)"; \ echo " 💡 Доступные образы: $(DOCKER_IMAGES)"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo ""; \ echo " 🔄 make docker rebuild - полная пересборка с очисткой кеша"; \ echo " 💡 Очищает: все локальные образы и кеш"; \ echo " 💡 Пересобирает: все образы с нуля"; \ echo " 💡 Полезно: при проблемах с кешем или зависимостями"; \ echo " 💡 Выполняет: clean + clean-builder + setup-builder + build"; \ echo ""; \ echo " 📤 make docker push - отправить образы в Docker Hub"; \ echo " 💡 Требует: docker login"; \ echo " 💡 Отправляет: все образы в registry inecs"; \ echo ""; \ echo " 📥 make docker pull - загрузить образы из Docker Hub"; \ echo " 💡 Загружает: все образы из registry inecs"; \ echo " 💡 Пропускает: отсутствующие образы"; \ echo ""; \ echo " 🧹 make docker clean - удалить локальные образы и кеш builds"; \ echo " 💡 Удаляет: все образы inecs/ansible-lab:*"; \ echo " 💡 Очищает: кеш builds (exec.cachemount, source.local, git.checkout)"; \ echo " 💡 Сохраняет: другие builds в системе"; \ echo " 💡 Безопасно: игнорирует ошибки"; \ echo ""; \ echo " 🧹 make docker clean-builder - очистка multi-arch builder"; \ echo " 💡 Удаляет: builder контейнер и buildkit контейнеры"; \ echo " 💡 Полезно: при проблемах со сборкой"; \ echo ""; \ echo " 🔧 make docker setup-builder - настройка multi-arch builder"; \ echo " 💡 Создает: builder в контейнере (не в системе)"; \ echo " 💡 Поддерживает: amd64 и arm64 архитектуры"; \ echo " 💡 Безопасно: использует inspect вместо buildx ls"; \ echo ""; \ echo " 🔍 make docker diagnose - диагностика buildx проблем"; \ echo " 💡 Проверяет: версии, контексты, builder, registry"; \ echo " 💡 Показывает: рекомендации по устранению проблем"; \ echo ""; \ echo " 🔄 make docker reset-builder - сброс buildx builder"; \ echo " 💡 Удаляет: старый builder и buildkit контейнеры"; \ echo " 💡 Создает: новый builder с предварительной загрузкой образа"; \ echo ""; \ echo " 📊 make docker info - информация о собранных образах"; \ echo " 💡 Показывает: размер, дата создания, теги"; \ echo ""; \ echo " 🔄 make docker update - обновить все образы"; \ echo " 💡 Выполняет: pull + build + push"; \ echo " 💡 Полный цикл обновления"; \ echo ""; \ echo " 💥 make docker purge - ПОЛНАЯ очистка Docker"; \ echo " ⚠️ УДАЛЯЕТ: все контейнеры, образы, тома, сети"; \ echo " ⚠️ ОСТАНОВИТ: все запущенные контейнеры"; \ echo " ⚠️ ТРЕБУЕТ: подтверждение пользователя"; \ echo "";; \ esac # ============================================================================= # ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ДЛЯ DOCKER # ============================================================================= # Надежная проверка существования buildx builder без использования buildx ls # Использует docker buildx inspect вместо buildx ls для избежания зависаний .PHONY: docker-check-builder docker-check-builder: @echo "🔍 Проверка buildx builder $(DOCKER_BUILDX_BUILDER)..." @if docker buildx inspect $(DOCKER_BUILDX_BUILDER) >/dev/null 2>&1; then \ echo "✅ Builder $(DOCKER_BUILDX_BUILDER) существует и готов"; \ exit 0; \ else \ echo "❌ Builder $(DOCKER_BUILDX_BUILDER) не найден"; \ exit 1; \ fi # Безопасное создание buildx builder с предварительной очисткой .PHONY: docker-create-builder docker-create-builder: @echo "🔧 Создание buildx builder $(DOCKER_BUILDX_BUILDER)..." @echo "📦 Предварительная загрузка образа buildkit..." @docker pull moby/buildkit:buildx-stable-1 || echo "⚠️ Не удалось загрузить moby/buildkit:buildx-stable-1, будет использован авто-пулл" @echo "🗑️ Удаление существующего builder (если есть)..." @docker buildx rm -f $(DOCKER_BUILDX_BUILDER) 2>/dev/null || true @echo "📦 Создание нового builder..." @docker buildx create \ --name $(DOCKER_BUILDX_BUILDER) \ --driver docker-container \ --driver-opt image=moby/buildkit:buildx-stable-1 \ --use || exit 1 @echo "⏳ Ожидание запуска buildkit контейнера..." @sleep 3 @echo "🔍 Проверка готовности builder..." @docker buildx inspect --builder $(DOCKER_BUILDX_BUILDER) --bootstrap >/dev/null || exit 1 @echo "✅ Builder $(DOCKER_BUILDX_BUILDER) создан и готов к работе" # Диагностика проблем с buildx .PHONY: docker-diagnose-buildx docker-diagnose-buildx: @echo "🔍 ДИАГНОСТИКА BUILDX ПРОБЛЕМ" @echo "==========================================" @echo "" @echo "📊 1. Версии Docker и Buildx:" @docker version --format "Docker: {{.Server.Version}}" 2>/dev/null || echo "❌ Docker недоступен" @docker buildx version 2>/dev/null || echo "❌ Buildx недоступен" @echo "" @echo "📋 2. Docker контексты (поиск мертвых tcp://):" @docker context ls 2>/dev/null || echo "❌ Не удалось получить список контекстов" @echo "" @echo "🔍 3. Проверка builder $(DOCKER_BUILDX_BUILDER):" @if docker buildx inspect $(DOCKER_BUILDX_BUILDER) >/dev/null 2>&1; then \ echo "✅ Builder $(DOCKER_BUILDX_BUILDER) существует"; \ docker buildx inspect $(DOCKER_BUILDX_BUILDER) --bootstrap >/dev/null 2>&1 && echo "✅ Builder готов" || echo "❌ Builder не готов"; \ else \ echo "❌ Builder $(DOCKER_BUILDX_BUILDER) не найден"; \ fi @echo "" @echo "🐳 4. Buildkit контейнеры:" @docker ps -a --filter "name=buildx_buildkit" --format "table {{.Names}}\t{{.Status}}\t{{.CreatedAt}}" 2>/dev/null || echo "❌ Не удалось получить список контейнеров" @echo "" @echo "🌐 5. Проверка доступа к registry:" @echo "📥 Тест загрузки moby/buildkit:buildx-stable-1..." @timeout 30 docker pull moby/buildkit:buildx-stable-1 >/dev/null 2>&1 && echo "✅ Доступ к registry OK" || echo "❌ Проблемы с доступом к registry" @echo "" @echo "🔧 6. Docker socket доступ:" @if [ -S /var/run/docker.sock ]; then \ echo "✅ Docker socket доступен: /var/run/docker.sock"; \ ls -la /var/run/docker.sock; \ else \ echo "❌ Docker socket недоступен: /var/run/docker.sock"; \ fi @echo "" @echo "💡 РЕКОМЕНДАЦИИ:" @echo " - Если buildx ls зависает: удалите мертвые контексты (docker context rm )" @echo " - Если pull зависает: настройте прокси или используйте mirror registry" @echo " - Если builder не создается: проверьте права доступа к Docker socket" @echo " - Для полной очистки: make docker clean-builder && make docker-create-builder" # Быстрая очистка и пересоздание builder .PHONY: docker-reset-builder docker-reset-builder: @echo "🔄 Сброс buildx builder..." @echo "🗑️ Удаление builder $(DOCKER_BUILDX_BUILDER)..." @docker buildx rm -f $(DOCKER_BUILDX_BUILDER) 2>/dev/null || true @echo "🧹 Очистка buildkit контейнеров..." @docker ps -a --filter "name=buildx_buildkit" --format "{{.Names}}" | xargs -r docker rm -f 2>/dev/null || true @echo "📦 Создание нового builder..." @$(MAKE) docker-create-builder @echo "✅ Builder сброшен и готов к работе" # Извлечение тега из базового образа docker-get-base-tag: @if [ -z "$(IMAGE)" ]; then \ echo "❌ Ошибка: IMAGE не указан"; \ exit 1; \ fi; \ case "$(IMAGE)" in \ alt-linux) \ BASE_IMAGE="alt:p9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ astra-linux) \ BASE_IMAGE="registry.astralinux.ru/library/astra/ubi17:1.7.6.uu2"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ redos) \ BASE_IMAGE="registry.red-soft.ru/ubi7/ubi"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ rhel) \ BASE_IMAGE="registry.access.redhat.com/ubi8/ubi"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos7) \ BASE_IMAGE="centos:7"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos8) \ BASE_IMAGE="quay.io/centos/centos:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos9) \ BASE_IMAGE="quay.io/centos/centos:stream9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ alma) \ BASE_IMAGE="almalinux:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ rocky) \ BASE_IMAGE="rockylinux:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu20) \ BASE_IMAGE="ubuntu:20.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu22) \ BASE_IMAGE="ubuntu:22.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu24) \ BASE_IMAGE="ubuntu:24.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian9) \ BASE_IMAGE="debian:9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian10) \ BASE_IMAGE="debian:10"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian11) \ BASE_IMAGE="debian:11"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian12) \ BASE_IMAGE="debian:bookworm"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ansible-controller) \ TAG="latest";; \ k8s) \ TAG="latest";; \ *) \ echo "❌ Неизвестный образ: $(IMAGE)"; \ exit 1;; \ esac; \ echo "$$TAG" # Сборка одного образа с multi-arch docker-build-image: @if [ -z "$(IMAGE)" ]; then \ echo "❌ Ошибка: IMAGE не указан"; \ exit 1; \ fi; \ TAG=$$($(MAKE) docker-get-base-tag IMAGE=$(IMAGE)); \ if [ "$(IMAGE)" = "redos" ] || [ "$(IMAGE)" = "astra-linux" ]; then \ PLATFORMS="linux/amd64"; \ echo ""; \ echo "=========================================="; \ echo "🔨 СБОРКА ОБРАЗА: $(DOCKER_REGISTRY):$(IMAGE)-$$TAG"; \ echo "📋 Платформы: $$PLATFORMS (только AMD64)"; \ echo "📋 Тег: $$TAG"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "⚠️ ВНИМАНИЕ: Базовый образ поддерживает только AMD64"; \ echo "=========================================="; \ else \ PLATFORMS="$(DOCKER_PLATFORMS)"; \ echo ""; \ echo "=========================================="; \ echo "🔨 СБОРКА ОБРАЗА: $(DOCKER_REGISTRY):$(IMAGE)-$$TAG"; \ echo "📋 Платформы: $$PLATFORMS"; \ echo "📋 Тег: $$TAG"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "=========================================="; \ fi; \ echo ""; \ cd dockerfiles/$(IMAGE) && \ docker buildx build \ --platform $$PLATFORMS \ --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG \ --tag $(DOCKER_REGISTRY):$(IMAGE)-latest \ --push \ .; \ echo ""; \ echo "✅ УСПЕШНО: $(DOCKER_REGISTRY):$(IMAGE)-$$TAG собран и отправлен"; \ echo "==========================================" # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С ANSIBLE-CONTROLLER # ============================================================================= controller: @case "$(word 2, $(MAKECMDGOALS))" in \ build) \ echo "🔨 Сборка ansible-controller (multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ $(MAKE) docker setup-builder; \ cd dockerfiles/ansible-controller && \ docker buildx build \ --platform $(DOCKER_PLATFORMS) \ --tag $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION) \ --tag $(DOCKER_REGISTRY):ansible-controller-latest \ --push \ .; \ echo "✅ ansible-controller собран и отправлен";; \ rebuild) \ echo "🔄 Пересборка ansible-controller с исправлениями..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ echo "🔧 Исправления: добавлен passlib для хеширования паролей"; \ $(MAKE) docker setup-builder; \ cd dockerfiles/ansible-controller && \ docker buildx build \ --platform $(DOCKER_PLATFORMS) \ --tag $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION) \ --tag $(DOCKER_REGISTRY):ansible-controller-latest \ --push \ --no-cache \ .; \ echo "✅ ansible-controller пересобран с исправлениями";; \ run) \ echo "🚀 Запуск ansible-controller..."; \ cd dockerfiles/ansible-controller && docker-compose up -d; \ echo "✅ ansible-controller запущен";; \ stop) \ echo "🛑 Остановка ansible-controller..."; \ cd dockerfiles/ansible-controller && docker-compose down; \ echo "✅ ansible-controller остановлен";; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🔨 make controller build - собрать ansible-controller (multi-arch)"; \ echo " 💡 Собирает: inecs/ansible-lab:ansible-controller-latest"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo " 💡 Использует: dockerfiles/ansible-controller/Dockerfile"; \ echo " 💡 Requirements: dockerfiles/ansible-controller/requirements.yml"; \ echo ""; \ echo " 🔄 make controller rebuild - пересобрать ansible-controller с исправлениями"; \ echo " 💡 Пересобирает: с --no-cache для применения исправлений"; \ echo " 💡 Исправления: добавлен passlib для хеширования паролей"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ echo ""; \ echo " 🚀 make controller run - запустить ansible-controller"; \ echo " 💡 Запускает: docker-compose up -d"; \ echo " 💡 Использует: dockerfiles/ansible-controller/docker-compose.yml"; \ echo ""; \ echo " 🛑 make controller stop - остановить ansible-controller"; \ echo " 💡 Останавливает: docker-compose down"; \ echo " 💡 Удаляет: контейнеры и сети";; \ esac # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С KUBERNETES KIND # ============================================================================= k8s: @case "$(word 2, $(MAKECMDGOALS))" in \ create) \ echo "☸️ Создание Kind кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ PRESET=k8s-minimal; \ echo "📋 Используется preset по умолчанию: $$PRESET (минимальный без аддонов)"; \ else \ PRESET=$$PRESET_ARG; \ echo "📋 Используется preset: $$PRESET"; \ fi; \ if [ ! -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ echo "❌ Ошибка: Пресет '$$PRESET' не найден!"; \ echo "💡 Доступные пресеты:"; \ ls -1 molecule/presets/k8s/*.yml 2>/dev/null | sed 's|molecule/presets/k8s/||g' | sed 's|\.yml||g' | sed 's/^/ - /' || echo " - k8s-minimal"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ docker run -d --name $$CONTAINER_NAME --rm \ -v "$(PWD):/workspace" -w /workspace \ -v /var/run/docker.sock:/var/run/docker.sock \ -u root \ -e ANSIBLE_FORCE_COLOR=1 \ -e MOLECULE_PRESET=$$PRESET \ -e MOLECULE_EPHEMERAL_DIRECTORY=/tmp/molecule_workspace \ $(DOCKER_K8S_IMAGE) \ /bin/bash -c 'sleep infinity'; \ echo "🚀 Запуск создания кластера..."; \ docker exec $$CONTAINER_NAME bash -c "cd /workspace && python3 /workspace/scripts/create_k8s_cluster.py molecule/presets/k8s/$$PRESET.yml $$CONTAINER_NAME"; \ echo "✅ Kind кластер создан"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ echo "🗑️ Удаление Kind кластера и контейнеров..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ PRESET=$${PRESET_ARG:-k8s-minimal}; \ CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ else \ echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ fi; \ docker rm -f $$CONTAINER_NAME 2>/dev/null || true; \ echo "🗑️ Удаление контейнеров из пресета..."; \ if [ -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec $$CONTAINER_NAME bash -c "python3 /workspace/scripts/delete_hosts.py /workspace/molecule/presets/k8s/$$PRESET.yml" 2>/dev/null || true; \ else \ python3 scripts/delete_hosts.py molecule/presets/k8s/$$PRESET.yml 2>/dev/null || true; \ fi; \ fi; \ echo "✅ Удаление завершено";; \ stop) \ echo "🛑 Остановка Kind кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s stop kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind stop cluster --name {}" 2>/dev/null || true; \ echo "✅ Kind кластер остановлен"; \ else \ echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ fi; \ echo "💡 Кластер остановлен, но не удален"; \ echo "💡 Для перезапуска: make k8s start $$PRESET_ARG"; \ echo "💡 Для полного удаления: make k8s destroy $$PRESET_ARG";; \ start) \ echo "🚀 Запуск Kind кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s start kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind start cluster --name {}" 2>/dev/null || true; \ echo "✅ Kind кластер запущен";; \ status) \ echo "📊 Статус Kind кластеров:"; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s status kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec $$CONTAINER_NAME bash -c "kind get clusters" 2>/dev/null || echo " Нет кластеров"; \ docker exec $$CONTAINER_NAME bash -c "kind get clusters | while read cluster; do echo \"Кластер: \$$cluster\"; kubectl --context kind-\$$cluster get nodes 2>/dev/null || true; done" 2>/dev/null || true; \ else \ echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ fi;; \ config) \ echo "📋 Получение kubeconfig..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s config kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ KUBECONFIG_FILE="$$(pwd)/kubeconfig"; \ docker exec $$CONTAINER_NAME bash -c "kind get kubeconfig" > $$KUBECONFIG_FILE 2>/dev/null || true; \ if [ -f $$KUBECONFIG_FILE ] && [ -s $$KUBECONFIG_FILE ]; then \ echo "✅ kubeconfig сохранен в: $$KUBECONFIG_FILE"; \ echo ""; \ echo "💡 Для использования:"; \ echo " export KUBECONFIG=$$KUBECONFIG_FILE"; \ echo " kubectl get nodes"; \ echo ""; \ echo "💡 Или для однократного использования:"; \ echo " kubectl --kubeconfig=$$KUBECONFIG_FILE get nodes"; \ else \ echo "❌ Не удалось получить kubeconfig"; \ rm -f $$KUBECONFIG_FILE; \ fi;; \ addon) \ echo "📦 Установка аддона..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ MANIFEST_ARG="$(word 4, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ exit 1; \ fi; \ if [ -z "$$MANIFEST_ARG" ]; then \ echo "❌ Ошибка: Укажите URL манифеста"; \ echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ echo "📥 Установка аддона из $$MANIFEST_ARG..."; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ echo "✅ Аддон установлен";; \ nodes) \ echo "🖥️ Просмотр узлов кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s nodes kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify get nodes";; \ shell) \ echo "🐚 Открытие shell в контейнере..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ echo "💡 Пример: make k8s shell kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec -it $$CONTAINER_NAME bash; \ else \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ fi;; \ manifest) \ echo "📄 Работа с манифестами..."; \ MANIFEST_CMD="$(word 3, $(MAKECMDGOALS))"; \ PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ MANIFEST_ARG="$(word 5, $(MAKECMDGOALS))"; \ if [ -z "$$MANIFEST_CMD" ] || [ -z "$$PRESET_ARG" ] || [ -z "$$MANIFEST_ARG" ]; then \ echo "❌ Ошибка: Укажите команду, пресет и путь к манифесту"; \ echo "💡 Пример: make k8s manifest apply kubernetes https://example.com/manifest.yaml"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ case "$$MANIFEST_CMD" in \ apply) \ echo "📥 Применение манифеста: $$MANIFEST_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG";; \ delete) \ echo "🗑️ Удаление ресурсов из манифеста: $$MANIFEST_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG";; \ update) \ echo "🔄 Обновление манифеста: $$MANIFEST_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG --force";; \ *) \ echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ echo "💡 Доступные команды: apply, delete, update"; \ exit 1;; \ esac;; \ helm) \ echo "📦 Работа с Helm..."; \ HELM_CMD="$(word 3, $(MAKECMDGOALS))"; \ PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ RELEASE_ARG="$(word 5, $(MAKECMDGOALS))"; \ CHART_ARG="$(word 6, $(MAKECMDGOALS))"; \ if [ -z "$$HELM_CMD" ] || [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите команду и пресет"; \ echo "💡 Пример: make k8s helm list kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ case "$$HELM_CMD" in \ apply) \ if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза и чарт"; \ echo "💡 Пример: make k8s helm apply kubernetes my-release stable/nginx-ingress"; \ exit 1; \ fi; \ echo "📦 Установка Helm чарта: $$CHART_ARG как $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ delete) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ echo "💡 Пример: make k8s helm delete kubernetes my-release"; \ exit 1; \ fi; \ echo "🗑️ Удаление Helm релиза: $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ update) \ if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза и чарт"; \ echo "💡 Пример: make k8s helm update kubernetes my-release stable/nginx-ingress"; \ exit 1; \ fi; \ echo "🔄 Обновление Helm релиза: $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ rollback) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ echo "💡 Пример: make k8s helm rollback kubernetes my-release"; \ exit 1; \ fi; \ echo "⏪ Откат Helm релиза: $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm rollback $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ list) \ echo "📋 Список Helm релизов:"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm list --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy --all-namespaces 2>&1 | grep -v '^WARNING' || true";; \ status) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ echo "💡 Пример: make k8s helm status kubernetes my-release"; \ exit 1; \ fi; \ echo "📊 Статус Helm релиза: $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm status $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ *) \ echo "❌ Неизвестная команда: $$HELM_CMD"; \ echo "💡 Доступные команды: apply, delete, update, rollback, list, status"; \ exit 1;; \ esac;; \ helmrepo) \ echo "🏪 Работа с Helm репозиториями..."; \ REPO_CMD="$(word 3, $(MAKECMDGOALS))"; \ PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ NAME_ARG="$(word 5, $(MAKECMDGOALS))"; \ URL_ARG="$(word 6, $(MAKECMDGOALS))"; \ if [ -z "$$REPO_CMD" ] || [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите команду и пресет"; \ echo "💡 Пример: make k8s helmrepo list kubernetes"; \ exit 1; \ fi; \ CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ exit 1; \ fi; \ case "$$REPO_CMD" in \ add) \ if [ -z "$$NAME_ARG" ] || [ -z "$$URL_ARG" ]; then \ echo "❌ Ошибка: Укажите имя и URL репозитория"; \ echo "💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ exit 1; \ fi; \ echo "➕ Добавление Helm репозитория: $$NAME_ARG"; \ docker exec $$CONTAINER_NAME bash -c "helm repo add $$NAME_ARG $$URL_ARG 2>&1 | grep -v '^WARNING' || true; helm repo update";; \ list) \ echo "📋 Список Helm репозиториев:"; \ docker exec $$CONTAINER_NAME bash -c "helm repo list 2>&1 | grep -v '^WARNING' || true";; \ delete) \ if [ -z "$$NAME_ARG" ]; then \ echo "❌ Ошибка: Укажите имя репозитория"; \ echo "💡 Пример: make k8s helmrepo delete kubernetes stable"; \ exit 1; \ fi; \ echo "🗑️ Удаление Helm репозитория: $$NAME_ARG"; \ docker exec $$CONTAINER_NAME bash -c "helm repo remove $$NAME_ARG 2>&1 | grep -v '^WARNING' || true";; \ update) \ echo "🔄 Обновление Helm репозиториев"; \ docker exec $$CONTAINER_NAME bash -c "helm repo update 2>&1 | grep -v '^WARNING' || true";; \ packages) \ if [ -z "$$NAME_ARG" ]; then \ echo "❌ Ошибка: Укажите имя репозитория"; \ echo "💡 Пример: make k8s helmrepo packages kubernetes stable"; \ exit 1; \ fi; \ echo "📦 Пакеты в репозитории: $$NAME_ARG"; \ docker exec $$CONTAINER_NAME bash -c "helm search repo $$NAME_ARG 2>&1 | grep -v '^WARNING' || true";; \ *) \ echo "❌ Неизвестная команда: $$REPO_CMD"; \ echo "💡 Доступные команды: add, list, delete, update, packages"; \ exit 1;; \ esac;; \ portforward) \ PORTFWD_CMD="$(word 3, $(MAKECMDGOALS))"; \ PORT_ARG="$(word 4, $(MAKECMDGOALS))"; \ if [ -z "$$PORTFWD_CMD" ]; then \ echo "❌ Ошибка: Укажите команду"; \ echo "💡 Пример: make k8s portforward create"; \ exit 1; \ fi; \ case "$$PORTFWD_CMD" in \ create) \ echo "🔌 Создание port-forward..."; \ python3 scripts/portforward.py create;; \ list) \ echo "📋 Список активных port-forward..."; \ python3 scripts/portforward.py list;; \ clear) \ echo "🗑️ Очистка всех port-forward..."; \ python3 scripts/portforward.py clear;; \ recreate) \ echo "🔄 Пересоздание port-forward..."; \ python3 scripts/portforward.py recreate;; \ delete) \ if [ -z "$$PORT_ARG" ]; then \ echo "❌ Ошибка: Укажите порт"; \ echo "💡 Пример: make k8s portforward delete 3000"; \ exit 1; \ fi; \ echo "🗑️ Удаление port-forward на порту $$PORT_ARG..."; \ python3 scripts/portforward.py delete $$PORT_ARG;; \ *) \ echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ echo "💡 Доступные команды: create, list, clear, recreate, delete"; \ exit 1;; \ esac;; \ *) \ echo "☸️ Доступные команды:"; \ echo ""; \ echo " make k8s create [preset] - создать Kind кластер"; \ echo " 💡 Без параметра: используется k8s-minimal (без аддонов)"; \ echo " 💡 С параметром: используется указанный пресет"; \ echo " 💡 Кластер НЕ удаляется автоматически"; \ echo ""; \ echo " make k8s destroy [preset] - удалить Kind кластер полностью"; \ echo " 💡 Удалит: кластер и контейнер ansible-controller"; \ echo ""; \ echo " make k8s stop [cluster] - остановить Kind кластер (без удаления)"; \ echo " 💡 Можно указать имя кластера или остановить все"; \ echo " 💡 Для перезапуска: make k8s start"; \ echo ""; \ echo " make k8s start [cluster] - запустить остановленный кластер"; \ echo " 💡 Можно указать имя кластера или запустить все"; \ echo ""; \ echo " make k8s status [cluster] - показать статус кластеров"; \ echo " 💡 Можно указать имя конкретного кластера"; \ echo ""; \ echo " make k8s config [cluster] - получить kubeconfig для подключения"; \ echo " 💡 Сохраняет: kubeconfig в корне проекта"; \ echo " 💡 Можно указать имя конкретного кластера"; \ echo ""; \ echo " make k8s addon [preset] [url] - установить аддон из манифеста"; \ echo " 💡 Требует: пресет и URL манифеста"; \ echo " 💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ echo ""; \ echo " make k8s nodes [preset] - показать узлы кластера"; \ echo " 💡 Требует: пресет"; \ echo " 💡 Пример: make k8s nodes kubernetes"; \ echo ""; \ echo " make k8s shell [preset] - открыть shell в контейнере"; \ echo " 💡 Для: ручного управления kubectl/kind"; \ echo " 💡 Пример: make k8s shell kubernetes"; \ echo ""; \ echo " make k8s manifest [cmd] [preset] [url] - работа с манифестами"; \ echo " 💡 Команды: apply, delete, update"; \ echo " 💡 Пример: make k8s manifest apply kubernetes https://example.com/deploy.yaml"; \ echo ""; \ echo " make k8s helm [cmd] [preset] [release] [chart] - работа с Helm"; \ echo " 💡 Команды: apply, delete, update, rollback, list, status"; \ echo " 💡 Пример: make k8s helm apply kubernetes nginx stable/nginx-ingress"; \ echo ""; \ echo " make k8s helmrepo [cmd] [preset] [name] [url] - работа с Helm репозиториями"; \ echo " 💡 Команды: add, list, delete, update, packages"; \ echo " 💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ echo ""; \ echo "💡 Примеры:"; \ echo " make k8s create # создать минимальный кластер"; \ echo " make k8s create kubernetes # создать кластер с аддонами"; \ echo " make k8s nodes kubernetes # показать узлы кластера"; \ echo " make k8s config kubernetes # получить kubeconfig для кластера"; \ echo " export KUBECONFIG=kubeconfig # использовать конфиг"; \ echo " kubectl get nodes # проверить узлы"; \ echo " make k8s addon kubernetes https://example.com/manifest.yaml # установить аддон"; \ echo " make k8s stop kubernetes # остановить кластер"; \ echo " make k8s start kubernetes # запустить кластер"; \ echo " make k8s destroy kubernetes # удалить кластер с пресетом kubernetes";; \ esac # ============================================================================= # СПРАВКА # ============================================================================= help: @echo "==========================================" @echo "AnsibleLab - Универсальная система" @echo "тестирования Ansible ролей" @echo "==========================================" @echo "" @echo "📁 Структура проекта:" @echo " scripts/ - Скрипты автоматизации" @echo " inventory/ - Инвентори файлы" @echo " molecule/default/ - Molecule конфигурация" @echo " roles/ - Ansible роли" @echo " vault/ - Зашифрованные секреты" @echo " dockerfiles/ - Docker образы для тестирования" @echo "" @echo "🚀 ОСНОВНЫЕ КОМАНДЫ:" @echo " make role lint [role] - проверить синтаксис ролей (все или конкретную)" @echo " 💡 Примеры: make role lint, make role lint devops" @echo " make role test [preset] - протестировать роли с preset'ом" @echo " make role deploy - развернуть роли на реальные серверы" @echo " make role list - показать все роли" @echo " make role create - создать новую роль (интерактивно)" @echo " make role delete - удалить роль (интерактивно)" @echo "" @echo "📖 ДОКУМЕНТАЦИЯ:" @echo " docs/deploy-yml-customization.md - полное руководство по кастомизации" @echo " docs/linting-guide.md - руководство по линтингу ролей" @echo "" @echo "🔍 ЛИНТИНГ РОЛЕЙ:" @echo " make role lint [role] - проверить синтаксис (все или конкретную роль)" @echo " 💡 Профили: production, basic, min" @echo " 💡 Конфигурация: .ansible-lint" @echo " 💡 Валидация: автоматическая проверка существования роли" @echo "" @echo "📋 PRESET'Ы (тестовые окружения):" @echo " make presets list - показать все доступные preset'ы" @echo " make presets info - подробная информация о preset'е" @echo " make presets test - запустить тест с preset'ом" @echo "" @echo "🖼️ СОБСТВЕННЫЕ ОБРАЗЫ (AnsibleLab):" @echo " make custom-images test [minimal|full|performance] - тест с собственными образами" @echo " make custom-images check - проверить наличие собственных образов" @echo " make custom-images build - собрать все образы для тестирования" @echo "" @echo "🐳 DOCKER ОБРАЗЫ (Multi-Arch):" @echo " make docker prepare - подготовка к работе с Docker Hub" @echo " make docker build - собрать все Docker образы (amd64 + arm64)" @echo " make docker build-image IMAGE=<имя> - собрать отдельный образ" @echo " make docker rebuild - полная пересборка с очисткой кеша" @echo " make docker push - отправить образы в Docker Hub" @echo " make docker pull - загрузить образы из Docker Hub" @echo " make docker clean - удалить локальные образы" @echo " make docker info - информация о собранных образах" @echo " make docker update - обновить все образы (pull + build + push)" @echo " make docker purge - ПОЛНАЯ очистка Docker (ОСТОРОЖНО!)" @echo "" @echo "🔧 DOCKER BUILDER (Multi-Arch):" @echo " make docker setup-builder - настройка multi-arch builder в контейнере" @echo " make docker clean-builder - очистка multi-arch builder" @echo " make docker diagnose - диагностика buildx проблем" @echo " make docker reset-builder - сброс buildx builder" @echo " 💡 Поддерживает: amd64, arm64, riscv64, ppc64le, s390x, 386, arm/v7, arm/v6" @echo "" @echo "🔧 АВТОМАТИЗАЦИЯ:" @echo " make update-playbooks - обновление playbook'ов при добавлении ролей" @echo " make generate-docs - генерация документации для ролей" @echo " make setup-cicd - настройка CI/CD для всех платформ" @echo " 💡 Безопасно: использует inspect вместо buildx ls (избегает зависаний)" @echo "" @echo "🔐 VAULT (управление секретами):" @echo " make vault init - инициализировать vault (создать vault/.vault)" @echo " make vault create - создать новый файл секретов" @echo " make vault edit - редактировать существующие секреты" @echo " make vault show - показать содержимое секретов" @echo " make vault delete - удалить файл секретов" @echo " make vault encrypt - зашифровать файл" @echo " make vault decrypt - расшифровать файл" @echo " make vault rekey - сменить пароль шифрования" @echo " make vault check - проверить vault файлы" @echo " make vault scan - поиск потенциальных секретов" @echo "" @echo "🌿 GIT (управление версиями):" @echo " make git push - отправить изменения в репозиторий" @echo " make git pull - получить изменения из репозитория" @echo " make git new - создать новую ветку" @echo "" @echo "🎮 CONTROLLER (ansible-controller Multi-Arch):" @echo " make controller build - собрать ansible-controller (amd64 + arm64)" @echo " make controller run - запустить ansible-controller" @echo " make controller stop - остановить ansible-controller" @echo "" @echo "☸️ KUBERNETES (Kind кластеры):" @echo " make k8s create [preset] - создать Kind кластер (по умолчанию: k8s-minimal)" @echo " make k8s destroy [preset] - удалить Kind кластер" @echo " make k8s start [preset] - запустить Kind кластер" @echo " make k8s stop [preset] - остановить Kind кластер" @echo " make k8s status [preset] - показать статус кластера" @echo " make k8s nodes [preset] - показать узлы кластера" @echo " make k8s config [preset] - получить kubeconfig для подключения" @echo " make k8s addon [preset] [url] - установить аддон из манифеста" @echo " make k8s shell [preset] - открыть shell в контейнере k8s" @echo "" @echo "💡 ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ:" @echo " make presets list # показать все preset'ы" @echo " make presets test PRESET=etcd-patroni # тест с etcd-patroni" @echo " make role test minimal # быстрый тест" @echo " make role test all-images # тест всех образов" @echo " make docker setup-builder # настройка multi-arch builder" @echo " make docker diagnose # диагностика buildx проблем" @echo " make docker reset-builder # сброс buildx builder" @echo " make docker build # собрать все образы (amd64 + arm64)" @echo " make docker rebuild # полная пересборка с очисткой кеша" @echo " make controller build # собрать ansible-controller (multi-arch)" @echo " make docker clean-builder # очистка builder'а" @echo " make docker purge # полная очистка Docker" @echo " make vault init # инициализировать vault" @echo " make vault create # создать секреты" @echo "" @echo "📖 Подробная справка: make [команда]" @echo "==========================================" # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С СОБСТВЕННЫМИ ОБРАЗАМИ # ============================================================================= custom-images: @case "$(word 2, $(MAKECMDGOALS))" in \ test) \ echo "🧪 Тестирование с собственными образами AnsibleLab..."; \ if [ -z "$(word 3, $(MAKECMDGOALS))" ]; then \ echo "💡 Использование: make custom-images test [minimal|full|performance]"; \ echo "💡 По умолчанию: minimal"; \ ./scripts/test-custom-images.sh minimal; \ else \ ./scripts/test-custom-images.sh $(word 3, $(MAKECMDGOALS)); \ fi;; \ check) \ echo "🔍 Проверка наличия собственных образов..."; \ ./scripts/test-custom-images.sh check;; \ build) \ echo "🔨 Сборка всех образов для тестирования..."; \ $(MAKE) docker build;; \ *) \ echo "🎯 Доступные команды:"; \ echo ""; \ echo " 🧪 make custom-images test [minimal|full|performance] - тест с собственными образами"; \ echo " 💡 minimal - минимальный тест (4 хоста)"; \ echo " 💡 full - полный тест (все образы)"; \ echo " 💡 performance - тест производительности (8 хостов)"; \ echo ""; \ echo " 🔍 make custom-images check - проверить наличие собственных образов"; \ echo " 💡 Показывает: какие образы есть, какие отсутствуют"; \ echo " 💡 Предлагает: команды для сборки отсутствующих образов"; \ echo ""; \ echo " 🔨 make custom-images build - собрать все образы для тестирования"; \ echo " 💡 Выполняет: make docker build"; \ echo " 💡 Собирает: все образы AnsibleLab"; \ echo ""; \ echo "💡 Пресеты для тестирования:"; \ echo " - custom-minimal.yml - минимальный тест (4 хоста)"; \ echo " - custom-images.yml - полный тест (все образы)"; \ echo " - custom-performance.yml - тест производительности (8 хостов)"; \ echo ""; \ echo "💡 Собственные образы:"; \ echo " - inecs/ansible-lab:ansible-controller-latest"; \ echo " - inecs/ansible-lab:alt-linux-latest"; \ echo " - inecs/ansible-lab:astra-linux-latest"; \ echo " - inecs/ansible-lab:redos-latest"; \ echo " - inecs/ansible-lab:rhel-latest"; \ echo " - inecs/ansible-lab:centos-latest"; \ echo " - inecs/ansible-lab:alma-latest"; \ echo " - inecs/ansible-lab:rocky-latest"; \ echo " - inecs/ansible-lab:ubuntu-latest"; \ echo " - inecs/ansible-lab:debian-latest";; \ esac # ============================================================================= # АВТОМАТИЗАЦИЯ # ============================================================================= update-playbooks: @echo "🔄 Обновление playbook'ов..." @chmod +x scripts/update-playbooks.sh @./scripts/update-playbooks.sh generate-docs: @echo "📚 Генерация документации..." @chmod +x scripts/generate-role-docs.sh @./scripts/generate-role-docs.sh setup-cicd: @echo "🔧 Настройка CI/CD..." @chmod +x scripts/setup-cicd.sh @./scripts/setup-cicd.sh # Очистка контейнеров Molecule .PHONY: clean-containers clean-containers: @echo "🧹 Очистка контейнеров Molecule..." @echo "📋 Поиск контейнеров проекта..." @docker ps -a --filter "ancestor=inecs/ansible-lab" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || true @echo "" @echo "🗑️ Удаление контейнеров..." @docker ps -a --filter "ancestor=inecs/ansible-lab" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true @docker ps -a --filter "network=labnet" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true @echo "🧹 Очистка сетей..." @docker network rm labnet 2>/dev/null || true @echo "✅ Очистка завершена" # Пустые цели для совместимости view create edit show delete lint deploy new advanced list info test build push pull clean prepare update run stop purge clean-builder setup-builder diagnose reset-builder build-image: @true # Универсальный таргет для всех пресетов и других динамических целей %: @: