- Introduce checks for existing GPG keys and repositories for Docker, NodeSource, and Tailscale to ensure correct configurations before installation. - Implement conditional removal of incorrect keys and repositories to maintain a clean setup. - Update Makefile to include a command for editing group vault variables. These changes enhance package management reliability and streamline the installation process across different roles.
531 lines
20 KiB
Makefile
531 lines
20 KiB
Makefile
.PHONY: help bootstrap lint test check apply dev 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: ## 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
|
|
|
|
apply: ## Run the development playbook on all dev hosts
|
|
@echo "$(YELLOW)Applying development playbook...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV)
|
|
|
|
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)
|
|
|
|
# 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
|
|
|
|
apps: ## Install applications only
|
|
@echo "$(YELLOW)Installing applications...$(RESET)"
|
|
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags apps
|
|
|
|
# Connectivity targets
|
|
ping: ## 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
|
|
|
|
test-connectivity: ## Test network connectivity and SSH access to all hosts
|
|
@echo "$(BOLD)Connectivity Test$(RESET)"
|
|
@if [ -n "$(CURRENT_HOST)" ]; then \
|
|
echo "$(BLUE)Auto-excluding current host: $(CURRENT_HOST) ($(CURRENT_IP))$(RESET)"; \
|
|
fi
|
|
@echo ""
|
|
@echo "$(YELLOW)Network Connectivity:$(RESET)"
|
|
@ansible-inventory --list | jq -r '._meta.hostvars | to_entries[] | select(.value.ansible_host) | "\(.key) \(.value.ansible_host)"' 2>/dev/null | while read host ip; do \
|
|
if [ "$$host" != "$(CURRENT_HOST)" ]; then \
|
|
printf " %-20s " "$$host ($$ip)"; \
|
|
if ping -c 1 -W 2 $$ip >/dev/null 2>&1; then \
|
|
echo "$(GREEN)✓ Network OK$(RESET)"; \
|
|
else \
|
|
echo "$(RED)✗ Network FAIL$(RESET)"; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
@echo ""
|
|
@echo "$(YELLOW)SSH Connectivity:$(RESET)"
|
|
@ansible all -m ping --one-line $(EXCLUDE_CURRENT) 2>/dev/null | grep -E "(SUCCESS|UNREACHABLE)" | while read line; do \
|
|
host=$$(echo "$$line" | cut -d' ' -f1); \
|
|
if echo "$$line" | grep -q "SUCCESS"; then \
|
|
printf " $(GREEN)✓ %-20s$(RESET) SSH OK\n" "$$host"; \
|
|
elif echo "$$line" | grep -q "UNREACHABLE"; then \
|
|
printf " $(RED)✗ %-20s$(RESET) SSH FAIL\n" "$$host"; \
|
|
fi; \
|
|
done
|
|
@echo ""
|
|
@echo "$(YELLOW)SSH Keys:$(RESET)"
|
|
@if [ -f ~/.ssh/id_ed25519.pub ]; then \
|
|
echo " $(GREEN)✓ SSH key available$(RESET) (id_ed25519)"; \
|
|
elif [ -f ~/.ssh/id_rsa.pub ]; then \
|
|
echo " $(GREEN)✓ SSH key available$(RESET) (id_rsa)"; \
|
|
else \
|
|
echo " $(RED)✗ No SSH key found$(RESET)"; \
|
|
echo " $(YELLOW) Run: ssh-keygen -t ed25519$(RESET)"; \
|
|
fi
|
|
|
|
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)"
|