#!/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