- Refactor Makefile to enhance command structure, including clearer descriptions and usage examples for targets related to development, inventory, and monitoring tasks. - Update inventory files to ensure correct host configurations and user settings, including adjustments to ansible_user for specific hosts. - Modify group_vars to streamline Tailscale configuration and ensure proper handling of authentication keys. These changes improve the clarity and usability of the Makefile and inventory setup, facilitating smoother operations across the infrastructure.
548 lines
21 KiB
Makefile
548 lines
21 KiB
Makefile
.PHONY: help bootstrap lint test check dev datascience inventory inventory-all local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring
|
|
.DEFAULT_GOAL := help
|
|
|
|
## Colors for output
|
|
BOLD := \033[1m
|
|
RED := \033[31m
|
|
GREEN := \033[32m
|
|
YELLOW := \033[33m
|
|
BLUE := \033[34m
|
|
RESET := \033[0m
|
|
|
|
# Playbook paths
|
|
PLAYBOOK_SITE := playbooks/site.yml
|
|
PLAYBOOK_DEV := playbooks/development.yml
|
|
PLAYBOOK_LOCAL := playbooks/local.yml
|
|
PLAYBOOK_MAINTENANCE := playbooks/maintenance.yml
|
|
PLAYBOOK_TAILSCALE := playbooks/tailscale.yml
|
|
PLAYBOOK_PROXMOX := playbooks/infrastructure/proxmox-vm.yml
|
|
|
|
# Collection and requirement paths
|
|
COLLECTIONS_REQ := collections/requirements.yml
|
|
PYTHON_REQ := requirements.txt
|
|
|
|
# Inventory paths
|
|
INVENTORY := inventories/production
|
|
INVENTORY_HOSTS := $(INVENTORY)/hosts
|
|
|
|
# Common ansible-playbook command with options
|
|
ANSIBLE_PLAYBOOK := ansible-playbook -i $(INVENTORY)
|
|
ANSIBLE_ARGS := --vault-password-file ~/.ansible-vault-pass
|
|
# Note: sudo passwords are in vault files as ansible_become_password
|
|
|
|
## Auto-detect current host to exclude from remote operations
|
|
CURRENT_IP := $(shell hostname -I | awk '{print $$1}')
|
|
CURRENT_HOST := $(shell ansible-inventory --list | jq -r '._meta.hostvars | to_entries[] | select(.value.ansible_host == "$(CURRENT_IP)") | .key' 2>/dev/null | head -1)
|
|
EXCLUDE_CURRENT := $(if $(CURRENT_HOST),--limit '!$(CURRENT_HOST)',)
|
|
|
|
help: ## Show this help message
|
|
@echo "$(BOLD)Ansible Development Environment$(RESET)"
|
|
@echo ""
|
|
@echo "$(BOLD)Available commands:$(RESET)"
|
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(BLUE)%-15s$(RESET) %s\n", $$1, $$2}'
|
|
@echo ""
|
|
@echo "$(BOLD)Examples:$(RESET)"
|
|
@echo " make bootstrap # Set up dependencies"
|
|
@echo " make check # Dry run all hosts"
|
|
@echo " make apply # Run on all dev hosts"
|
|
@echo " make dev HOST=dev01 # Run on specific host"
|
|
@echo " make local # Run local playbook"
|
|
@echo " make maintenance # Run maintenance on all hosts"
|
|
@echo " make maintenance GROUP=dev # Run maintenance on dev group"
|
|
@echo " make maintenance HOST=dev01 # Run maintenance on specific host"
|
|
@echo " make maintenance CHECK=true # Dry-run maintenance on all hosts"
|
|
@echo " make maintenance VERBOSE=true # Run with verbose output"
|
|
@echo " make maintenance-verbose GROUP=dev # Verbose maintenance on dev group"
|
|
@echo ""
|
|
|
|
bootstrap: ## Install all project dependencies from requirements files
|
|
@echo "$(BOLD)Installing Project Dependencies$(RESET)"
|
|
@echo ""
|
|
@echo "$(YELLOW)Python Requirements ($(PYTHON_REQ)):$(RESET)"
|
|
@if [ -f "$(PYTHON_REQ)" ]; then \
|
|
if command -v pipx >/dev/null 2>&1; then \
|
|
printf " %-30s " "Installing with pipx"; \
|
|
if pipx install -r $(PYTHON_REQ) >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ Installed$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)⚠ Some packages may have failed$(RESET)"; \
|
|
fi; \
|
|
elif command -v pip3 >/dev/null 2>&1; then \
|
|
printf " %-30s " "Installing with pip3 --user"; \
|
|
if pip3 install --user -r $(PYTHON_REQ) >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ Installed$(RESET)"; \
|
|
else \
|
|
printf " %-30s " "Trying with --break-system-packages"; \
|
|
if pip3 install --break-system-packages -r $(PYTHON_REQ) >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ Installed$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ Failed$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
else \
|
|
printf " %-30s " "Python packages"; \
|
|
echo "$(YELLOW)⚠ Skipped (pip3/pipx not found)$(RESET)"; \
|
|
fi; \
|
|
else \
|
|
printf " %-30s " "$(PYTHON_REQ)"; \
|
|
echo "$(RED)✗ File not found$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@echo "$(YELLOW)Node.js Dependencies (package.json):$(RESET)"
|
|
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
|
|
printf " %-30s " "Installing npm packages"; \
|
|
if npm install >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ Installed$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ Failed$(RESET)"; \
|
|
fi; \
|
|
else \
|
|
printf " %-30s " "npm packages"; \
|
|
echo "$(YELLOW)⚠ Skipped (package.json or npm not found)$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@echo "$(YELLOW)Ansible Collections ($(COLLECTIONS_REQ)):$(RESET)"
|
|
@if [ -f "$(COLLECTIONS_REQ)" ]; then \
|
|
ansible-galaxy collection install -r $(COLLECTIONS_REQ) 2>&1 | grep -E "(Installing|Skipping|ERROR)" | while read line; do \
|
|
if echo "$$line" | grep -q "Installing"; then \
|
|
collection=$$(echo "$$line" | awk '{print $$2}' | sed 's/:.*//'); \
|
|
printf " $(GREEN)✓ %-30s$(RESET) Installed\n" "$$collection"; \
|
|
elif echo "$$line" | grep -q "Skipping"; then \
|
|
collection=$$(echo "$$line" | awk '{print $$2}' | sed 's/,.*//'); \
|
|
printf " $(BLUE)- %-30s$(RESET) Already installed\n" "$$collection"; \
|
|
elif echo "$$line" | grep -q "ERROR"; then \
|
|
printf " $(RED)✗ Error: $$line$(RESET)\n"; \
|
|
fi; \
|
|
done || ansible-galaxy collection install -r $(COLLECTIONS_REQ); \
|
|
else \
|
|
printf " %-30s " "$(COLLECTIONS_REQ)"; \
|
|
echo "$(RED)✗ File not found$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
|
|
lint: ## Run ansible-lint on all playbooks and roles
|
|
@echo "$(YELLOW)Running ansible-lint...$(RESET)"
|
|
ansible-lint
|
|
@echo "$(GREEN)✓ Linting completed$(RESET)"
|
|
|
|
test-syntax: ## Run comprehensive syntax and validation checks
|
|
@echo "$(BOLD)Comprehensive Testing$(RESET)"
|
|
@echo ""
|
|
@echo "$(YELLOW)Playbook Syntax:$(RESET)"
|
|
@for playbook in $(PLAYBOOK_DEV) $(PLAYBOOK_LOCAL) $(PLAYBOOK_MAINTENANCE) $(PLAYBOOK_TAILSCALE); do \
|
|
if [ -f "$$playbook" ]; then \
|
|
printf " %-25s " "$$playbook"; \
|
|
if ansible-playbook "$$playbook" --syntax-check >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ FAIL$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
@echo ""
|
|
@echo "$(YELLOW)Infrastructure Playbooks:$(RESET)"
|
|
@for playbook in playbooks/infrastructure/*.yml; do \
|
|
if [ -f "$$playbook" ]; then \
|
|
printf " %-25s " "$$playbook"; \
|
|
if ansible-playbook "$$playbook" --syntax-check >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ FAIL$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
@echo ""
|
|
@echo "$(YELLOW)Role Test Playbooks:$(RESET)"
|
|
@for test_playbook in roles/*/tests/test.yml; do \
|
|
if [ -f "$$test_playbook" ]; then \
|
|
role_name=$$(echo "$$test_playbook" | cut -d'/' -f2); \
|
|
printf " %-25s " "roles/$$role_name/test"; \
|
|
if ansible-playbook "$$test_playbook" --syntax-check >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ FAIL$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
@echo ""
|
|
@echo "$(YELLOW)Markdown Validation:$(RESET)"
|
|
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
|
|
printf " %-25s " "Markdown syntax"; \
|
|
if npm run test:markdown >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)⚠ Issues$(RESET)"; \
|
|
fi; \
|
|
else \
|
|
printf " %-25s " "Markdown syntax"; \
|
|
echo "$(YELLOW)⚠ npm/package.json not available$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@echo "$(YELLOW)Documentation Links:$(RESET)"
|
|
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
|
|
printf " %-25s " "Link validation"; \
|
|
if npm run test:links >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)⚠ Issues$(RESET)"; \
|
|
fi; \
|
|
else \
|
|
printf " %-25s " "Link validation"; \
|
|
echo "$(YELLOW)⚠ npm/package.json not available$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@echo "$(YELLOW)Configuration Validation:$(RESET)"
|
|
@for yaml_file in inventories/production/group_vars/all/main.yml; do \
|
|
if [ -f "$$yaml_file" ]; then \
|
|
printf " %-25s " "$$yaml_file (YAML)"; \
|
|
if python3 -c "import yaml; yaml.safe_load(open('$$yaml_file'))" >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ FAIL$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
@printf " %-25s " "ansible.cfg (INI)"; \
|
|
if python3 -c "import configparser; c=configparser.ConfigParser(); c.read('ansible.cfg')" >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ FAIL$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
|
|
test: ## Run all tests (lint + syntax check if available)
|
|
@if command -v ansible-lint >/dev/null 2>&1; then \
|
|
echo "$(YELLOW)Running ansible-lint...$(RESET)"; \
|
|
ansible-lint 2>/dev/null && echo "$(GREEN)✓ Linting completed$(RESET)" || echo "$(YELLOW)⚠ Linting had issues$(RESET)"; \
|
|
echo ""; \
|
|
fi
|
|
@$(MAKE) test-syntax
|
|
|
|
check: auto-fallback ## Dry-run the development playbook (--check mode)
|
|
@echo "$(YELLOW)Running dry-run on development hosts...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --check --diff
|
|
|
|
check-local: ## Dry-run the local playbook
|
|
@echo "$(YELLOW)Running dry-run on localhost...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) --check --diff -K
|
|
|
|
site: ## Run the complete site playbook
|
|
@echo "$(YELLOW)Running complete site deployment...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SITE)
|
|
|
|
local: ## Run the local playbook on localhost
|
|
@echo "$(YELLOW)Applying local playbook...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) -K
|
|
|
|
# Host-specific targets
|
|
dev: ## Run on specific host (usage: make dev HOST=dev01)
|
|
ifndef HOST
|
|
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
|
@echo "Usage: make dev HOST=dev01"
|
|
@exit 1
|
|
endif
|
|
@echo "$(YELLOW)Running on host: $(HOST)$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST)
|
|
|
|
# Data science role
|
|
datascience: ## Install data science stack (usage: make datascience HOST=server01)
|
|
ifndef HOST
|
|
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
|
@echo "Usage: make datascience HOST=server01"
|
|
@exit 1
|
|
endif
|
|
@echo "$(YELLOW)Installing data science stack on: $(HOST)$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST) --tags datascience
|
|
|
|
# Inventory system
|
|
inventory: ## Show installed software on host (usage: make inventory HOST=dev01)
|
|
ifndef HOST
|
|
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
|
@echo "Usage: make inventory HOST=dev01"
|
|
@exit 1
|
|
endif
|
|
@echo "$(BOLD)Software Inventory for: $(HOST)$(RESET)"
|
|
@echo ""
|
|
@ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \
|
|
user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \
|
|
if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \
|
|
scp -q scripts/inventory.sh $$user@$$ip:/tmp/inventory.sh 2>/dev/null && \
|
|
ssh $$user@$$ip 'bash /tmp/inventory.sh; rm /tmp/inventory.sh' 2>/dev/null; \
|
|
else \
|
|
echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \
|
|
fi
|
|
|
|
inventory-all: ## Show installed software on all hosts
|
|
@echo "$(BOLD)Software Inventory - All Hosts$(RESET)\n"
|
|
@for host in $$(ansible all --list-hosts 2>/dev/null | grep -v "hosts" | tr -d ' '); do \
|
|
echo "$(YELLOW)=== $$host ===$(RESET)"; \
|
|
ansible $$host -m script -a "scripts/inventory.sh" 2>/dev/null | sed -n '/CHANGED/,$$p' | tail -n +3 || echo "$(RED)Failed to connect$(RESET)"; \
|
|
echo ""; \
|
|
done
|
|
|
|
# Tag-based execution
|
|
security: ## Run only security-related roles
|
|
@echo "$(YELLOW)Running security roles...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags security
|
|
|
|
# Unified maintenance target with intelligent parameter detection
|
|
maintenance: ## Run maintenance (usage: make maintenance [GROUP=dev] [HOST=dev01] [SERIAL=1] [CHECK=true] [VERBOSE=true])
|
|
@$(MAKE) _maintenance-run
|
|
|
|
_maintenance-run:
|
|
@# Determine target and build command
|
|
@TARGET="all"; \
|
|
ANSIBLE_CMD="$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_MAINTENANCE)"; \
|
|
DESCRIPTION="all hosts"; \
|
|
NEED_SUDO=""; \
|
|
\
|
|
if [ -n "$(HOST)" ]; then \
|
|
TARGET="host $(HOST)"; \
|
|
ANSIBLE_CMD="$$ANSIBLE_CMD --limit $(HOST)"; \
|
|
DESCRIPTION="host $(HOST)"; \
|
|
if [ "$(HOST)" = "localhost" ]; then \
|
|
NEED_SUDO="-K"; \
|
|
fi; \
|
|
elif [ -n "$(GROUP)" ]; then \
|
|
TARGET="$(GROUP) group"; \
|
|
ANSIBLE_CMD="$$ANSIBLE_CMD -e target_group=$(GROUP)"; \
|
|
DESCRIPTION="$(GROUP) group"; \
|
|
if [ "$(GROUP)" = "local" ]; then \
|
|
NEED_SUDO="-K"; \
|
|
fi; \
|
|
else \
|
|
NEED_SUDO="-K"; \
|
|
fi; \
|
|
\
|
|
if [ -n "$(SERIAL)" ]; then \
|
|
ANSIBLE_CMD="$$ANSIBLE_CMD -e maintenance_serial=$(SERIAL)"; \
|
|
DESCRIPTION="$$DESCRIPTION (serial=$(SERIAL))"; \
|
|
fi; \
|
|
\
|
|
if [ "$(CHECK)" = "true" ]; then \
|
|
ANSIBLE_CMD="$$ANSIBLE_CMD --check --diff"; \
|
|
echo "$(YELLOW)Dry-run maintenance on $$DESCRIPTION...$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)Running maintenance on $$DESCRIPTION...$(RESET)"; \
|
|
fi; \
|
|
\
|
|
if [ "$(VERBOSE)" = "true" ]; then \
|
|
ANSIBLE_CMD="$$ANSIBLE_CMD -v"; \
|
|
echo "$(BLUE)Running with verbose output...$(RESET)"; \
|
|
fi; \
|
|
\
|
|
if [ -n "$(GROUP)" ] && [ "$(GROUP)" != "dev" ] && [ "$(GROUP)" != "local" ]; then \
|
|
echo "$(BLUE)Available groups: dev, gitea, portainer, homepage, ansible, local$(RESET)"; \
|
|
fi; \
|
|
\
|
|
$$ANSIBLE_CMD $$NEED_SUDO
|
|
|
|
# Legacy/convenience aliases
|
|
maintenance-dev: ## Run maintenance on dev group (legacy alias)
|
|
@$(MAKE) maintenance GROUP=dev
|
|
|
|
maintenance-all: ## Run maintenance on all hosts (legacy alias)
|
|
@$(MAKE) maintenance
|
|
|
|
maintenance-check: ## Dry-run maintenance (legacy alias, usage: make maintenance-check [GROUP=dev])
|
|
@$(MAKE) maintenance CHECK=true GROUP=$(GROUP)
|
|
|
|
maintenance-verbose: ## Run maintenance with verbose output (usage: make maintenance-verbose [GROUP=dev])
|
|
@$(MAKE) maintenance VERBOSE=true GROUP=$(GROUP)
|
|
|
|
docker: ## Install/configure Docker only
|
|
@echo "$(YELLOW)Running Docker setup...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags docker
|
|
|
|
shell: ## Configure shell only
|
|
@echo "$(YELLOW)Running shell configuration...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags shell
|
|
|
|
shell-all: ## Configure shell on all shell_hosts (usage: make shell-all)
|
|
@echo "$(YELLOW)Running shell configuration on all shell hosts...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) playbooks/shell.yml $(ANSIBLE_ARGS)
|
|
|
|
apps: ## Install applications only
|
|
@echo "$(YELLOW)Installing applications...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags apps
|
|
|
|
# Connectivity targets
|
|
ping: auto-fallback ## Ping hosts with colored output (usage: make ping [GROUP=dev] [HOST=dev01])
|
|
ifdef HOST
|
|
@echo "$(YELLOW)Pinging host: $(HOST)$(RESET)"
|
|
@ansible $(HOST) -m ping --one-line | while read line; do \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
echo "$(GREEN)✓ $$line$(RESET)"; \
|
|
elif echo "$$line" | grep -q "UNREACHABLE"; then \
|
|
echo "$(RED)✗ $$line$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)? $$line$(RESET)"; \
|
|
fi; \
|
|
done
|
|
else ifdef GROUP
|
|
@echo "$(YELLOW)Pinging $(GROUP) group...$(RESET)"
|
|
@ansible $(GROUP) -m ping --one-line | while read line; do \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
echo "$(GREEN)✓ $$line$(RESET)"; \
|
|
elif echo "$$line" | grep -q "UNREACHABLE"; then \
|
|
echo "$(RED)✗ $$line$(RESET)"; \
|
|
else \
|
|
echo "$(YELLOW)? $$line$(RESET)"; \
|
|
fi; \
|
|
done
|
|
else
|
|
@echo "$(YELLOW)Pinging all hosts...$(RESET)"
|
|
@if [ -n "$(CURRENT_HOST)" ]; then \
|
|
echo "$(BLUE)Auto-excluding current host: $(CURRENT_HOST) ($(CURRENT_IP))$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@ansible all -m ping --one-line $(EXCLUDE_CURRENT) 2>/dev/null | grep -E "(SUCCESS|UNREACHABLE)" > /tmp/ping_results.tmp; \
|
|
success_count=$$(grep -c "SUCCESS" /tmp/ping_results.tmp 2>/dev/null || echo 0); \
|
|
fail_count=$$(grep -c "UNREACHABLE" /tmp/ping_results.tmp 2>/dev/null || echo 0); \
|
|
while read line; do \
|
|
host=$$(echo "$$line" | cut -d' ' -f1); \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
printf "$(GREEN) ✓ %-20s$(RESET) Connected\n" "$$host"; \
|
|
elif echo "$$line" | grep -q "UNREACHABLE"; then \
|
|
reason=$$(echo "$$line" | grep -o "Permission denied.*" | head -1 || echo "Connection failed"); \
|
|
printf "$(RED) ✗ %-20s$(RESET) $$reason\n" "$$host"; \
|
|
fi; \
|
|
done < /tmp/ping_results.tmp; \
|
|
rm -f /tmp/ping_results.tmp; \
|
|
echo ""; \
|
|
printf "$(BOLD)Summary:$(RESET) $(GREEN)$$success_count connected$(RESET), $(RED)$$fail_count failed$(RESET)\n"
|
|
endif
|
|
|
|
facts: ## Gather facts from all hosts
|
|
@echo "$(YELLOW)Gathering facts...$(RESET)"
|
|
ansible all -m setup --tree /tmp/facts $(EXCLUDE_CURRENT)
|
|
|
|
show-current: ## Show current host that will be auto-excluded
|
|
@echo "$(BOLD)Current Host Detection:$(RESET)"
|
|
@echo " Current IP: $(BLUE)$(CURRENT_IP)$(RESET)"
|
|
@echo " Current Host: $(BLUE)$(CURRENT_HOST)$(RESET)"
|
|
@echo " Exclude Flag: $(BLUE)$(EXCLUDE_CURRENT)$(RESET)"
|
|
|
|
clean: ## Clean up ansible artifacts
|
|
@echo "$(YELLOW)Cleaning up artifacts...$(RESET)"
|
|
rm -rf .ansible/facts/
|
|
find . -name "*.retry" -delete
|
|
@echo "$(GREEN)✓ Cleanup completed$(RESET)"
|
|
|
|
# Debug targets
|
|
debug: ## Run with debug output enabled
|
|
@echo "$(YELLOW)Running with debug output...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) -e "ansible_debug_output=true"
|
|
|
|
verbose: ## Run with verbose output
|
|
@echo "$(YELLOW)Running with verbose output...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) -vv
|
|
|
|
# Quick development workflow
|
|
quick: test check ## Quick test and check before applying
|
|
@echo "$(GREEN)✓ Ready to apply changes$(RESET)"
|
|
|
|
# Tailscale management
|
|
tailscale: ## Install Tailscale on all machines
|
|
@echo "$(YELLOW)Installing Tailscale on all machines...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE)
|
|
@echo "$(GREEN)✓ Tailscale installation complete$(RESET)"
|
|
|
|
tailscale-check: ## Check Tailscale installation (dry-run)
|
|
@echo "$(YELLOW)Checking Tailscale installation...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE) --check --diff
|
|
@echo "$(GREEN)✓ Tailscale check complete$(RESET)"
|
|
|
|
tailscale-dev: ## Install Tailscale on dev machines only
|
|
@echo "$(YELLOW)Installing Tailscale on dev machines...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE) --limit dev
|
|
@echo "$(GREEN)✓ Tailscale installation on dev machines complete$(RESET)"
|
|
|
|
tailscale-status: ## Check Tailscale status on all machines
|
|
@echo "$(BOLD)Tailscale Status$(RESET)"
|
|
@if [ -n "$(CURRENT_HOST)" ]; then \
|
|
echo "$(BLUE)Auto-excluding current host: $(CURRENT_HOST) ($(CURRENT_IP))$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@ansible all -m shell -a "tailscale status --json | jq -r '.Self.DNSName + \" (\" + .Self.TailscaleIPs[0] + \") - \" + .BackendState'" --become $(EXCLUDE_CURRENT) 2>/dev/null | while read line; do \
|
|
host=$$(echo "$$line" | cut -d' ' -f1); \
|
|
status=$$(echo "$$line" | grep -o "Running\|Stopped\|NeedsLogin" || echo "Unknown"); \
|
|
ip=$$(echo "$$line" | grep -o "100\.[0-9.]*" || echo "No IP"); \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
result=$$(echo "$$line" | cut -d'>' -f2 | tr -d ' "'); \
|
|
printf " $(GREEN)✓ %-20s$(RESET) %s\n" "$$host" "$$result"; \
|
|
elif echo "$$line" | grep -q "FAILED\|UNREACHABLE"; then \
|
|
printf " $(RED)✗ %-20s$(RESET) Not available\n" "$$host"; \
|
|
fi; \
|
|
done || ansible all -m shell -a "tailscale status" --become $(EXCLUDE_CURRENT) 2>/dev/null | grep -E "(SUCCESS|FAILED)" | while read line; do \
|
|
host=$$(echo "$$line" | cut -d' ' -f1); \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
printf " $(GREEN)✓ %-20s$(RESET) Connected\n" "$$host"; \
|
|
else \
|
|
printf " $(RED)✗ %-20s$(RESET) Failed\n" "$$host"; \
|
|
fi; \
|
|
done
|
|
|
|
# Vault management
|
|
edit-vault: ## Edit encrypted host vars (usage: make edit-vault HOST=dev01)
|
|
ifndef HOST
|
|
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
|
@echo "Usage: make edit-vault HOST=dev01"
|
|
@exit 1
|
|
endif
|
|
ansible-vault edit host_vars/$(HOST).yml
|
|
|
|
edit-group-vault: ## Edit encrypted group vars (usage: make edit-group-vault)
|
|
ansible-vault edit inventories/production/group_vars/all/vault.yml
|
|
|
|
|
|
copy-ssh-key: ## Copy SSH key to specific host (usage: make copy-ssh-key HOST=giteaVM)
|
|
ifndef HOST
|
|
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
|
@echo "Usage: make copy-ssh-key HOST=giteaVM"
|
|
@exit 1
|
|
endif
|
|
@echo "$(YELLOW)Copying SSH key to $(HOST)...$(RESET)"
|
|
@ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \
|
|
user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \
|
|
if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \
|
|
echo "Target: $$user@$$ip"; \
|
|
ssh-copy-id $$user@$$ip; \
|
|
else \
|
|
echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \
|
|
echo "Check your inventory and host_vars"; \
|
|
fi
|
|
|
|
create-vault: ## Create encrypted vault file for secrets (passwords, auth keys, etc.)
|
|
@echo "$(YELLOW)Creating vault file for storing secrets...$(RESET)"
|
|
ansible-vault create group_vars/all/vault.yml
|
|
@echo "$(GREEN)✓ Vault file created. Add your secrets here (e.g. vault_tailscale_auth_key)$(RESET)"
|
|
|
|
create-vm: ## Create Ansible controller VM on Proxmox
|
|
@echo "$(YELLOW)Creating Ansible controller VM on Proxmox...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_PROXMOX) --ask-vault-pass
|
|
@echo "$(GREEN)✓ VM creation complete$(RESET)"
|
|
|
|
monitoring: ## Install monitoring tools on all machines
|
|
@echo "$(YELLOW)Installing monitoring tools...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags monitoring
|
|
@echo "$(GREEN)✓ Monitoring installation complete$(RESET)"
|
|
|
|
test-connectivity: ## Test host connectivity with detailed diagnostics and recommendations
|
|
@echo "$(YELLOW)Testing host connectivity...$(RESET)"
|
|
@if [ -f "test_connectivity.py" ]; then \
|
|
python3 test_connectivity.py --hosts-file $(INVENTORY_HOSTS); \
|
|
else \
|
|
echo "$(RED)Error: test_connectivity.py not found$(RESET)"; \
|
|
exit 1; \
|
|
fi
|
|
|
|
auto-fallback: ## Automatically switch to fallback IPs when primary IPs fail
|
|
@echo "$(YELLOW)Auto-fallback: Testing and switching to working IPs...$(RESET)"
|
|
@if [ -f "auto-fallback.sh" ]; then \
|
|
chmod +x auto-fallback.sh && ./auto-fallback.sh; \
|
|
else \
|
|
echo "$(RED)Error: auto-fallback.sh not found$(RESET)"; \
|
|
exit 1; \
|
|
fi
|