veda/apps/validate-manifests.sh

282 lines
9.7 KiB
Bash
Executable File

#!/bin/bash
# Kubernetes/Helm Configuration Validator
# Validates all applications without deploying them
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Counters
TOTAL=0
PASSED=0
FAILED=0
echo -e "${BLUE}=== Kubernetes Configuration Validator ===${NC}\n"
# Function to validate a Helm chart
validate_helm_chart() {
local app_path=$1
local app_name=$(basename "$app_path")
local namespace=$2
TOTAL=$((TOTAL + 1))
echo -e "${YELLOW}[$TOTAL] Validating: $app_name (namespace: $namespace)${NC}"
# Check if Chart.yaml exists
if [ ! -f "$app_path/Chart.yaml" ]; then
echo -e "${YELLOW} → Not a Helm chart - skipping Helm validation${NC}\n"
TOTAL=$((TOTAL - 1))
return 0
fi
# Check if dependencies are built (build to temp location if not)
local temp_dir=""
if [ -f "$app_path/Chart.yaml" ] && grep -q "dependencies:" "$app_path/Chart.yaml"; then
if [ ! -d "$app_path/charts" ]; then
echo " → Dependencies not built - building to temporary location..."
# Create temp directory
temp_dir=$(mktemp -d)
# Copy chart to temp location (remove trailing slash if present)
local clean_path="${app_path%/}"
cp -r "$clean_path" "$temp_dir/"
local temp_chart="$temp_dir/$(basename "$clean_path")"
# Build dependencies in temp location
if ! (cd "$temp_chart" && helm dependency build > /dev/null 2>&1); then
echo -e "${RED} ✗ Failed to build dependencies${NC}\n"
rm -rf "$temp_dir"
FAILED=$((FAILED + 1))
return 1
fi
# Use temp location for validation
app_path="$temp_chart"
fi
fi
# Lint the chart
echo " → Running Helm lint..."
if ! (cd "$app_path" && helm lint . 2>&1 | grep -q "0 chart(s) failed"); then
echo -e "${RED} ✗ Helm lint failed${NC}"
(cd "$app_path" && helm lint .)
echo ""
FAILED=$((FAILED + 1))
return 1
fi
# Template the chart
echo " → Rendering Helm templates..."
# Try rendering with validation first (redirect to temp file to avoid hanging on large output)
local temp_output=$(mktemp)
if (cd "$app_path" && helm template "$app_name" . --namespace "$namespace" --validate > "$temp_output" 2>&1); then
template_exit=0
else
template_exit=$?
fi
if [ $template_exit -ne 0 ]; then
# Check if it's just CRD validation warnings
if grep -Eqi "(no matches for kind|ensure CRDs are installed)" "$temp_output"; then
echo -e "${YELLOW} ⚠ Template validation skipped - requires CRDs to be installed${NC}"
# Still try to render without validation
if (cd "$app_path" && helm template "$app_name" . --namespace "$namespace" > /dev/null 2>&1); then
# Rendering works without validation, this is acceptable
rm -f "$temp_output"
# Continue with other checks...
else
echo -e "${RED} ✗ Helm template rendering failed${NC}"
head -20 "$temp_output"
echo ""
rm -f "$temp_output"
FAILED=$((FAILED + 1))
return 1
fi
elif grep -qi "exists and cannot be imported into the current release" "$temp_output"; then
echo -e "${YELLOW} ⚠ Resource ownership validation skipped - resources may already exist in cluster${NC}"
# This is expected when resources already exist, try without validation
if (cd "$app_path" && helm template "$app_name" . --namespace "$namespace" > /dev/null 2>&1); then
rm -f "$temp_output"
# Continue with other checks...
else
echo -e "${RED} ✗ Helm template rendering failed${NC}"
head -20 "$temp_output"
echo ""
rm -f "$temp_output"
FAILED=$((FAILED + 1))
return 1
fi
else
echo -e "${RED} ✗ Helm template failed${NC}"
head -20 "$temp_output"
echo ""
rm -f "$temp_output"
FAILED=$((FAILED + 1))
return 1
fi
fi
rm -f "$temp_output"
# Validate with kubeval (if installed)
if command -v kubeval &> /dev/null; then
echo " → Validating manifests with kubeval..."
if ! (cd "$app_path" && helm template "$app_name" . --namespace "$namespace" | kubeval --ignore-missing-schemas > /dev/null 2>&1); then
echo -e "${YELLOW} ⚠ Kubeval warnings (may be acceptable)${NC}"
fi
fi
# Check for common issues
echo " → Checking for common issues..."
local rendered=$(cd "$app_path" && helm template "$app_name" . --namespace "$namespace" 2>&1)
# Check for placeholder secrets
if echo "$rendered" | grep -qi "changeme\|placeholder\|CHANGE_ME\|TODO"; then
echo -e "${YELLOW} ⚠ Warning: Found placeholder values (changeme/placeholder/TODO)${NC}"
fi
# Check for resource requests/limits
if ! echo "$rendered" | grep -q "resources:"; then
echo -e "${YELLOW} ⚠ Warning: No resource requests/limits found${NC}"
fi
# Cleanup temp directory if created
if [ -n "$temp_dir" ] && [ -d "$temp_dir" ]; then
rm -rf "$temp_dir"
fi
echo -e "${GREEN} ✓ Validation passed${NC}\n"
PASSED=$((PASSED + 1))
return 0
}
# Function to validate an ArgoCD Application manifest
validate_argocd_app() {
local app_file=$1
local app_name=$(basename "$(dirname "$app_file")")
TOTAL=$((TOTAL + 1))
echo -e "${YELLOW}[$TOTAL] Validating ArgoCD Application: $app_name${NC}"
# Check YAML syntax using yq or basic validation
if command -v yq &> /dev/null; then
if ! yq eval '.' "$app_file" > /dev/null 2>&1; then
echo -e "${RED} ✗ Invalid YAML syntax${NC}\n"
FAILED=$((FAILED + 1))
return 1
fi
elif ! grep -q "^apiVersion:" "$app_file"; then
echo -e "${RED} ✗ Invalid YAML - missing apiVersion${NC}\n"
FAILED=$((FAILED + 1))
return 1
fi
# Check for required fields
local missing_fields=()
grep -q "kind: Application" "$app_file" || missing_fields+=("kind: Application")
grep -q "metadata:" "$app_file" || missing_fields+=("metadata")
grep -q "spec:" "$app_file" || missing_fields+=("spec")
grep -q "source:" "$app_file" || missing_fields+=("source")
grep -q "destination:" "$app_file" || missing_fields+=("destination")
if [ ${#missing_fields[@]} -gt 0 ]; then
echo -e "${RED} ✗ Missing required fields: ${missing_fields[*]}${NC}\n"
FAILED=$((FAILED + 1))
return 1
fi
echo -e "${GREEN} ✓ Validation passed${NC}\n"
PASSED=$((PASSED + 1))
return 0
}
# Main validation flow
echo -e "${BLUE}Validating Monitoring Stack...${NC}\n"
# Thanos
if [ -d "monitoring/thanos" ]; then
validate_helm_chart "monitoring/thanos" "monitoring"
validate_argocd_app "monitoring/thanos/application.yaml"
fi
# Prometheus
if [ -d "monitoring/prometheus" ]; then
validate_helm_chart "monitoring/prometheus" "monitoring"
validate_argocd_app "monitoring/prometheus/application.yaml"
fi
# Grafana
if [ -d "monitoring/grafana" ]; then
validate_helm_chart "monitoring/grafana" "monitoring"
validate_argocd_app "monitoring/grafana/application.yaml"
fi
echo -e "${BLUE}Validating Logging Stack...${NC}\n"
# Loki
if [ -d "logging/loki" ]; then
validate_helm_chart "logging/loki" "logging"
validate_argocd_app "logging/loki/application.yaml"
fi
# Promtail
if [ -d "logging/promtail" ]; then
validate_helm_chart "logging/promtail" "logging"
validate_argocd_app "logging/promtail/application.yaml"
fi
# Additional apps (if they exist)
echo -e "${BLUE}Validating Other Applications...${NC}\n"
for app_dir in */; do
# Skip special directories
if [[ "$app_dir" == "monitoring/" ]] || [[ "$app_dir" == "logging/" ]]; then
continue
fi
# Check if it's a Helm chart
if [ -f "$app_dir/Chart.yaml" ] && [ -f "$app_dir/application.yaml" ]; then
app_name=$(basename "$app_dir")
# Try to extract namespace from application.yaml
namespace=$(grep -A 10 "destination:" "$app_dir/application.yaml" | grep "namespace:" | head -1 | awk '{print $2}')
[ -z "$namespace" ] && namespace="default"
validate_helm_chart "$app_dir" "$namespace"
validate_argocd_app "$app_dir/application.yaml"
fi
# Check for nested charts (like ceph/operator, ceph/cluster)
for nested_dir in "$app_dir"*/; do
if [ -f "$nested_dir/Chart.yaml" ] && [ -f "$nested_dir/application.yaml" ]; then
nested_name=$(basename "$nested_dir")
# Try to extract namespace from application.yaml
namespace=$(grep -A 10 "destination:" "$nested_dir/application.yaml" | grep "namespace:" | head -1 | awk '{print $2}')
[ -z "$namespace" ] && namespace="default"
validate_helm_chart "$nested_dir" "$namespace"
validate_argocd_app "$nested_dir/application.yaml"
fi
done
done
# Summary
echo -e "${BLUE}=== Validation Summary ===${NC}"
echo -e "Total checks: $TOTAL"
echo -e "${GREEN}Passed: $PASSED${NC}"
echo -e "${RED}Failed: $FAILED${NC}\n"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}✓ All validations passed!${NC}"
exit 0
else
echo -e "${RED}✗ Some validations failed. Please review the errors above.${NC}"
exit 1
fi