214 lines
6.1 KiB
Bash
214 lines
6.1 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
# Validate and optimize VM YAML configurations
|
||
|
|
# Ensures quota checks, non-compounded commands, and best practices
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||
|
|
VM_DIR="${PROJECT_ROOT}/examples/production"
|
||
|
|
|
||
|
|
# Colors
|
||
|
|
GREEN='\033[0;32m'
|
||
|
|
YELLOW='\033[1;33m'
|
||
|
|
RED='\033[0;31m'
|
||
|
|
BLUE='\033[0;34m'
|
||
|
|
NC='\033[0m'
|
||
|
|
|
||
|
|
log() {
|
||
|
|
echo -e "${BLUE}[$(date +'%H:%M:%S')]${NC} $*"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_success() {
|
||
|
|
echo -e "${GREEN}[$(date +'%H:%M:%S')] ✅${NC} $*"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_warning() {
|
||
|
|
echo -e "${YELLOW}[$(date +'%H:%M:%S')] ⚠️${NC} $*"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_error() {
|
||
|
|
echo -e "${RED}[$(date +'%H:%M:%S')] ❌${NC} $*"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check if YAML file has required fields
|
||
|
|
check_yaml_structure() {
|
||
|
|
local file=$1
|
||
|
|
local errors=0
|
||
|
|
|
||
|
|
# Check for required fields
|
||
|
|
if ! grep -q "kind: ProxmoxVM" "$file"; then
|
||
|
|
log_error "$file: Missing 'kind: ProxmoxVM'"
|
||
|
|
errors=$((errors + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
if ! grep -q "tenant.sankofa.nexus/id" "$file"; then
|
||
|
|
log_warning "$file: Missing tenant label (may be intentional for infrastructure VMs)"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if ! grep -q "image:" "$file"; then
|
||
|
|
log_error "$file: Missing image specification"
|
||
|
|
errors=$((errors + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Check image format
|
||
|
|
if grep -q "image:" "$file"; then
|
||
|
|
local image=$(grep "image:" "$file" | head -1 | sed 's/.*image: *"\(.*\)".*/\1/')
|
||
|
|
if [[ -z "$image" ]]; then
|
||
|
|
log_error "$file: Image specification is empty"
|
||
|
|
errors=$((errors + 1))
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
return $errors
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for compounded commands (should be avoided)
|
||
|
|
check_compounded_commands() {
|
||
|
|
local file=$1
|
||
|
|
local warnings=0
|
||
|
|
|
||
|
|
# Check for problematic patterns
|
||
|
|
if grep -q "systemctl.*&&\|systemctl.*||" "$file"; then
|
||
|
|
log_warning "$file: Found compounded systemctl commands (should be separate)"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Check for command chains in runcmd
|
||
|
|
if grep -A 5 "runcmd:" "$file" | grep -q ".*&&.*"; then
|
||
|
|
log_warning "$file: Found command chains with && in runcmd (should be separate list items)"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Note: || true is acceptable for non-critical error handling
|
||
|
|
# But we should document this
|
||
|
|
|
||
|
|
return $warnings
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for quota check annotations
|
||
|
|
check_quota_annotations() {
|
||
|
|
local file=$1
|
||
|
|
local warnings=0
|
||
|
|
|
||
|
|
# Check if tenant label exists
|
||
|
|
if grep -q "tenant.sankofa.nexus/id" "$file"; then
|
||
|
|
local tenant=$(grep "tenant.sankofa.nexus/id" "$file" | sed 's/.*tenant.sankofa.nexus\/id: *"\(.*\)".*/\1/')
|
||
|
|
if [[ -n "$tenant" && "$tenant" != "infrastructure" ]]; then
|
||
|
|
# For tenant VMs, quota should be checked by controller
|
||
|
|
# But we can add annotation for explicit quota check
|
||
|
|
if ! grep -q "phoenix.sankofa.nexus/quota-check" "$file"; then
|
||
|
|
log_warning "$file: Tenant VM should have quota-check annotation (controller will check automatically)"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
return $warnings
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate image specification
|
||
|
|
check_image_spec() {
|
||
|
|
local file=$1
|
||
|
|
local errors=0
|
||
|
|
|
||
|
|
if grep -q "image:" "$file"; then
|
||
|
|
local image=$(grep "image:" "$file" | head -1 | sed 's/.*image: *"\(.*\)".*/\1/')
|
||
|
|
|
||
|
|
# Check for valid image format
|
||
|
|
if [[ ! "$image" =~ ^[a-zA-Z0-9._-]+$ ]]; then
|
||
|
|
log_error "$file: Invalid image format: $image"
|
||
|
|
errors=$((errors + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Check for recommended image
|
||
|
|
if [[ "$image" != "ubuntu-22.04-cloud" ]]; then
|
||
|
|
log_warning "$file: Using non-standard image: $image (recommended: ubuntu-22.04-cloud)"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
return $errors
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for best practices
|
||
|
|
check_best_practices() {
|
||
|
|
local file=$1
|
||
|
|
local warnings=0
|
||
|
|
|
||
|
|
# Check for QEMU guest agent
|
||
|
|
if ! grep -q "qemu-guest-agent" "$file"; then
|
||
|
|
log_warning "$file: Missing qemu-guest-agent package"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Check for guest agent enable/start
|
||
|
|
if ! grep -q "systemctl enable qemu-guest-agent" "$file"; then
|
||
|
|
log_warning "$file: Missing 'systemctl enable qemu-guest-agent'"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
if ! grep -q "systemctl start qemu-guest-agent" "$file"; then
|
||
|
|
log_warning "$file: Missing 'systemctl start qemu-guest-agent'"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Check for package verification
|
||
|
|
if ! grep -q "Verifying required packages" "$file"; then
|
||
|
|
log_warning "$file: Missing package verification step"
|
||
|
|
warnings=$((warnings + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
return $warnings
|
||
|
|
}
|
||
|
|
|
||
|
|
# Main validation
|
||
|
|
main() {
|
||
|
|
log "Validating and optimizing VM YAML configurations..."
|
||
|
|
echo
|
||
|
|
|
||
|
|
local total_files=0
|
||
|
|
local total_errors=0
|
||
|
|
local total_warnings=0
|
||
|
|
|
||
|
|
# Find all YAML files
|
||
|
|
while IFS= read -r -d '' file; do
|
||
|
|
total_files=$((total_files + 1))
|
||
|
|
log "Checking: $file"
|
||
|
|
|
||
|
|
local file_errors=0
|
||
|
|
local file_warnings=0
|
||
|
|
|
||
|
|
check_yaml_structure "$file" || file_errors=$?
|
||
|
|
check_compounded_commands "$file" || file_warnings=$?
|
||
|
|
check_quota_annotations "$file" || file_warnings=$((file_warnings + $?))
|
||
|
|
check_image_spec "$file" || file_errors=$((file_errors + $?))
|
||
|
|
check_best_practices "$file" || file_warnings=$((file_warnings + $?))
|
||
|
|
|
||
|
|
total_errors=$((total_errors + file_errors))
|
||
|
|
total_warnings=$((total_warnings + file_warnings))
|
||
|
|
|
||
|
|
if [[ $file_errors -eq 0 && $file_warnings -eq 0 ]]; then
|
||
|
|
log_success "$file: All checks passed"
|
||
|
|
fi
|
||
|
|
echo
|
||
|
|
done < <(find "$VM_DIR" -name "*.yaml" -type f -print0)
|
||
|
|
|
||
|
|
# Summary
|
||
|
|
echo
|
||
|
|
log "=== Validation Summary ==="
|
||
|
|
echo "Files checked: $total_files"
|
||
|
|
echo "Errors: $total_errors"
|
||
|
|
echo "Warnings: $total_warnings"
|
||
|
|
echo
|
||
|
|
|
||
|
|
if [[ $total_errors -eq 0 ]]; then
|
||
|
|
log_success "All VM configurations are valid!"
|
||
|
|
return 0
|
||
|
|
else
|
||
|
|
log_error "Found $total_errors errors. Please fix before deployment."
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
main "$@"
|
||
|
|
|