Skip to content

Instantly share code, notes, and snippets.

@Laxman-SM
Forked from oguzhancoskun/cdc
Created April 8, 2025 13:14
Show Gist options
  • Save Laxman-SM/d138c28b3640e63490248d40faedc7ca to your computer and use it in GitHub Desktop.
Save Laxman-SM/d138c28b3640e63490248d40faedc7ca to your computer and use it in GitHub Desktop.
AWS EKS Cilium Prefix Delegation Checker
#!/bin/bash
# Colors and styles
BOLD='\033[1m'
DIM='\033[2m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# Icons (emojis and symbols)
CHECK_MARK="✓"
CROSS_MARK="✗"
INFO_ICON="ℹ"
WARN_ICON="⚠"
GEAR_ICON="⚙"
CLOUD_ICON="☁"
NET_ICON="🌐"
DEBUG_ICON="🔍"
FIX_ICON="🔧"
# Terminal width for dynamic sizing
TERM_WIDTH=$(tput cols)
# Print centered header
print_header() {
local text="$1"
local padding=$(( (TERM_WIDTH - ${#text}) / 2 ))
printf "\n%${padding}s$BOLD$BLUE%s$NC%${padding}s\n" "" "$text" ""
printf "%${TERM_WIDTH}s\n" "" | tr " " "─"
}
# Print status message
print_status() {
local icon="$1"
local color="$2"
local message="$3"
echo -e "$color$icon $message$NC"
}
# Print error with suggestions
print_error() {
local error_msg="$1"
local suggestions=("${@:2}")
echo
print_status "$CROSS_MARK" "$RED" "Error: $error_msg"
if [ ${#suggestions[@]} -gt 0 ]; then
echo
print_status "$DEBUG_ICON" "$YELLOW" "Possible causes and solutions:"
for suggestion in "${suggestions[@]}"; do
print_status "$FIX_ICON" "$CYAN" "$suggestion"
done
fi
}
# Print progress spinner
spinner() {
local pid=$1
local delay=0.1
local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
local temp=${spinstr#?}
printf " $CYAN[%c]$NC " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# Check AWS credentials
check_aws_credentials() {
if ! aws sts get-caller-identity --profile "$profile" &>/dev/null; then
print_error "AWS credentials check failed" \
"Ensure AWS CLI is configured with valid credentials for profile '$profile'" \
"Run 'aws configure --profile $profile' to set up credentials" \
"Check if your AWS credentials have expired" \
"Verify you have necessary IAM permissions for EKS and EC2"
exit 1
fi
}
# Check EKS cluster access
check_eks_access() {
if ! aws eks describe-cluster --name "$cluster_name" --profile "$profile" &>/dev/null; then
print_error "Cannot access EKS cluster" \
"Verify the cluster name '$cluster_name' is correct" \
"Ensure your AWS credentials have EKS permissions" \
"Check if the cluster exists in the current region" \
"Run 'aws eks update-kubeconfig --name $cluster_name --profile $profile' to update kubeconfig"
exit 1
fi
}
# Check AWS region and cluster details
check_cluster_details() {
print_header "Verifying Cluster Details"
# Get current AWS region
local region=$(aws configure get region --profile "$profile")
if [ -z "$region" ]; then
region=$(aws configure get default.region)
fi
print_status "$INFO_ICON" "$BLUE" "Current AWS Region: $region"
# Get cluster details
print_status "$CLOUD_ICON" "$CYAN" "Fetching cluster details..."
local cluster_details=$(aws eks describe-cluster \
--name "$cluster_name" \
--profile "$profile" \
--query 'cluster.[status,version,platformVersion,roleArn]' \
--output text 2>/dev/null)
if [ $? -eq 0 ]; then
read -r status version platform role <<< "$cluster_details"
print_status "$CHECK_MARK" "$GREEN" "Cluster Status: $status"
print_status "$INFO_ICON" "$BLUE" "Kubernetes Version: $version"
print_status "$INFO_ICON" "$BLUE" "Platform Version: $platform"
fi
# Check nodegroups
print_status "$CLOUD_ICON" "$CYAN" "Checking node groups..."
local nodegroups=$(aws eks list-nodegroups \
--cluster-name "$cluster_name" \
--profile "$profile" \
--query 'nodegroups[*]' \
--output text)
if [ -z "$nodegroups" ]; then
print_error "No node groups found in cluster" \
"Create a node group in the EKS cluster" \
"Check if node groups were deleted or failed to create" \
"Verify Auto Scaling Group configurations"
return 1
fi
print_status "$CHECK_MARK" "$GREEN" "Found node groups: $nodegroups"
# Check nodes directly via kubectl
print_status "$CLOUD_ICON" "$CYAN" "Checking Kubernetes nodes..."
# Update kubeconfig for the cluster
aws eks update-kubeconfig --name "$cluster_name" --profile "$profile" &>/dev/null
local nodes=$(kubectl get nodes -o json)
if [ $? -eq 0 ]; then
local node_count=$(echo "$nodes" | jq '.items | length')
print_status "$CHECK_MARK" "$GREEN" "Found $node_count Kubernetes nodes"
# Show node details
echo
print_status "$INFO_ICON" "$BLUE" "Node Details:"
echo "$nodes" | jq -r '.items[] | " \(.metadata.name): \(.status.conditions[] | select(.type=="Ready").status)"' | \
while read -r line; do
if [[ $line == *": True" ]]; then
print_status "$CHECK_MARK" "$GREEN" "$line"
else
print_status "$WARN_ICON" "$YELLOW" "$line"
fi
done
else
print_error "Failed to get Kubernetes nodes" \
"Run 'aws eks update-kubeconfig --name $cluster_name --profile $profile'" \
"Check if kubectl is properly configured" \
"Verify your IAM permissions for EKS"
return 1
fi
}
# Parse arguments
while [[ "$#" -gt 0 ]]; do
case "$1" in
--cluster-name|-c) cluster_name="$2"; shift ;;
--profile|-p) profile="$2"; shift ;;
--help|-h)
print_header "Usage Information"
echo -e "${CYAN}Usage: $0 --cluster-name <cluster_name> --profile <aws_profile>${NC}"
echo -e "\n${DIM}Options:${NC}"
echo -e " ${CYAN}--cluster-name, -c${NC} The name of the EKS cluster"
echo -e " ${CYAN}--profile, -p${NC} The AWS CLI profile to use"
echo -e " ${CYAN}--help, -h${NC} Show this help message"
echo -e "\n${DIM}Example:${NC}"
echo -e " $0 --cluster-name my-cluster --profile my-profile"
exit 0
;;
*)
print_error "Unknown option: $1" \
"Use --help to see available options"
exit 1
;;
esac
shift
done
# Check for required arguments
if [ -z "$cluster_name" ] || [ -z "$profile" ]; then
print_error "Missing required arguments" \
"Cluster name and AWS profile are required" \
"Use --help to see usage instructions"
exit 1
fi
# Check for required utilities
check_requirements() {
print_header "Checking Requirements"
local all_installed=true
for cmd in aws kubectl ipcalc jq; do
if ! command -v $cmd &>/dev/null; then
print_status "$CROSS_MARK" "$RED" "$cmd is not installed"
local install_instructions=""
case $cmd in
aws)
install_instructions="Run 'brew install awscli' or visit https://aws.amazon.com/cli/"
;;
kubectl)
install_instructions="Run 'brew install kubectl' or visit https://kubernetes.io/docs/tasks/tools/"
;;
ipcalc)
install_instructions="Run 'brew install ipcalc'"
;;
jq)
install_instructions="Run 'brew install jq'"
;;
esac
print_error "$cmd is required but not installed" \
"$install_instructions" \
"After installation, ensure the command is in your PATH"
all_installed=false
else
print_status "$CHECK_MARK" "$GREEN" "$cmd is installed"
fi
done
if [ "$all_installed" = false ]; then
exit 1
fi
}
# Enhanced fetch_instances function
fetch_instances() {
print_header "Fetching Cluster Instances"
print_status "$CLOUD_ICON" "$CYAN" "Cluster: $cluster_name"
print_status "$INFO_ICON" "$BLUE" "Profile: $profile"
# Check AWS credentials and cluster access first
check_aws_credentials
check_eks_access
# Check cluster details and nodes
check_cluster_details
print_status "$CLOUD_ICON" "$CYAN" "Fetching EC2 instances..."
# First try: Get instance ID from kubectl node name
print_status "$INFO_ICON" "$BLUE" "Attempting to get instance ID from node name..."
local node_name=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
if [[ $node_name =~ ^ip-([0-9-]+)\..*$ ]]; then
local private_ip=${BASH_REMATCH[1]//-/.}
print_status "$INFO_ICON" "$CYAN" "Found node private IP: $private_ip"
# Try to find instance by private IP
instances=$(aws ec2 describe-instances \
--filters "Name=private-ip-address,Values=$private_ip" \
--query "Reservations[].Instances[].InstanceId" \
--profile "$profile" \
--output text)
if [ ! -z "$instances" ]; then
print_status "$CHECK_MARK" "$GREEN" "Found instance by private IP: $instances"
return 0
fi
fi
# Second try: Different tag combinations
print_status "$INFO_ICON" "$BLUE" "Attempting to find instances by tags..."
local filters=(
"Name=tag:aws:eks:cluster-name,Values=$cluster_name"
"Name=tag:kubernetes.io/cluster/$cluster_name,Values=owned"
"Name=tag:eks:cluster-name,Values=$cluster_name"
)
for filter in "${filters[@]}"; do
print_status "$DEBUG_ICON" "$YELLOW" "Trying filter: $filter"
instances=$(aws ec2 describe-instances \
--filters "$filter" \
--query "Reservations[].Instances[].InstanceId" \
--profile "$profile" \
--output text) &
spinner $!
if [ ! -z "$instances" ]; then
print_status "$CHECK_MARK" "$GREEN" "Found $(echo $instances | wc -w) instances using filter: $filter"
break
fi
done
# Third try: List all instances with eks-related tags
if [ -z "$instances" ]; then
print_status "$INFO_ICON" "$BLUE" "Checking all EKS-related instances..."
local all_eks_instances=$(aws ec2 describe-instances \
--filters "Name=tag-key,Values=*eks*" \
--query 'Reservations[].Instances[].[InstanceId,PrivateIpAddress,Tags[?Key==`aws:eks:cluster-name`].Value|[0]]' \
--profile "$profile" \
--output table)
echo -e "\nFound EKS instances:"
echo "$all_eks_instances"
print_error "No instances found for cluster $cluster_name" \
"Node exists in Kubernetes but EC2 instance not found with cluster tags" \
"Private IP from node: $private_ip" \
"Check instance tags: aws:eks:cluster-name=$cluster_name" \
"Check instance tags: kubernetes.io/cluster/$cluster_name=owned" \
"Ensure you're using the correct AWS region" \
"The instance might be missing required EKS tags" \
"Try manually adding the tag: aws:eks:cluster-name=$cluster_name"
exit 1
fi
}
# Helper function to compare version strings
version_compare() {
local version1=$1
local version2=$2
# Remove 'v' prefix if present
version1=${version1#v}
version2=${version2#v}
# Split versions into arrays
IFS='.' read -ra ver1 <<< "$version1"
IFS='.' read -ra ver2 <<< "$version2"
# Compare major version
if [ ${ver1[0]} -gt ${ver2[0]} ]; then
echo 1
elif [ ${ver1[0]} -lt ${ver2[0]} ]; then
echo -1
else
# Compare minor version if major versions are equal
if [ ${ver1[1]} -gt ${ver2[1]} ]; then
echo 1
elif [ ${ver1[1]} -lt ${ver2[1]} ]; then
echo -1
else
# Compare patch version if minor versions are equal
if [ ${#ver1[@]} -gt 2 ] && [ ${#ver2[@]} -gt 2 ]; then
if [ ${ver1[2]} -gt ${ver2[2]} ]; then
echo 1
elif [ ${ver1[2]} -lt ${ver2[2]} ]; then
echo -1
else
echo 0
fi
else
echo 0
fi
fi
fi
}
# Helper function to check if prefix delegation can be enabled
check_prefix_delegation_requirements() {
local vpc_cni_version=$1
local node_selector=$2
print_status "$DEBUG_ICON" "$YELLOW" "Checking prefix delegation requirements..."
local requirements_met=true
local suggestions=()
# Check VPC CNI version requirements (minimum v1.9.0)
if [ ! -z "$vpc_cni_version" ]; then
local min_version="1.9.0"
local version_result=$(version_compare "$vpc_cni_version" "$min_version" 2>/dev/null)
if [ "$version_result" = "-1" ]; then
requirements_met=false
suggestions+=("Upgrade VPC CNI to version 1.9.0 or later (current: $vpc_cni_version)")
fi
fi
# Check nodeSelector configuration
if [ "$node_selector" != "null" ] && [ ! -z "$node_selector" ]; then
requirements_met=false
suggestions+=("Remove nodeSelector from aws-node DaemonSet using:")
suggestions+=("kubectl -n kube-system patch daemonset aws-node --type='strategic' -p='{\"spec\":{\"template\":{\"spec\":{\"nodeSelector\": null}}}}'")
fi
# Print results
if [ "$requirements_met" = true ]; then
print_status "$CHECK_MARK" "$GREEN" "All requirements for prefix delegation are met"
print_status "$INFO_ICON" "$CYAN" "To enable prefix delegation, set ENABLE_PREFIX_DELEGATION=true in aws-node DaemonSet"
print_status "$INFO_ICON" "$CYAN" "Run: kubectl -n kube-system set env daemonset aws-node ENABLE_PREFIX_DELEGATION=true"
else
print_status "$WARN_ICON" "$YELLOW" "Some requirements for prefix delegation are not met:"
for suggestion in "${suggestions[@]}"; do
print_status "$FIX_ICON" "$CYAN" "$suggestion"
done
fi
}
# Check ENIs for an instance
check_enis() {
print_header "Checking Cilium Prefix Delegation Status"
results=()
max_prefix_length=40
local total_enis=0
local total_prefixes=0
local total_secondary_ips=0
local delegation_manager=""
local delegation_status="INACTIVE"
# Add variables for tracking IP details
local total_available_ips=0
local total_used_ips=0
local max_pods_per_eni=0
local instance_type=""
# Get instance type for capacity planning
if [ ! -z "$instances" ]; then
instance_type=$(aws ec2 describe-instances \
--instance-ids $instances \
--query 'Reservations[].Instances[].InstanceType' \
--profile $profile \
--output text 2>/dev/null)
fi
# First check Cilium configuration
print_status "$INFO_ICON" "$BLUE" "Checking Cilium configuration..."
# Check for Cilium
local cilium_status=$(kubectl get ds -n kube-system cilium -o json 2>/dev/null)
local cilium_exists=$?
# Check for aws-node DaemonSet as fallback
local aws_node_status=$(kubectl get ds -n kube-system aws-node -o json 2>/dev/null)
local aws_node_exists=$?
local cni_type=""
local prefix_delegation_enabled=false
if [ $cilium_exists -eq 0 ]; then
cni_type="Cilium"
delegation_manager="cilium"
print_status "$CHECK_MARK" "$GREEN" "Cilium CNI is active"
# Check Cilium configuration for prefix delegation
local cilium_config=$(kubectl get configmap -n kube-system cilium-config -o yaml 2>/dev/null)
if echo "$cilium_config" | grep -q "enable-prefix-delegation: \"true\""; then
prefix_delegation_enabled=true
delegation_status="ACTIVE"
print_status "$CHECK_MARK" "$GREEN" "Prefix delegation is enabled (Managed by Cilium)"
# Get Cilium version
local cilium_version=$(kubectl get ds -n kube-system cilium -o jsonpath='{.spec.template.spec.containers[0].image}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' 2>/dev/null)
if [ ! -z "$cilium_version" ]; then
print_status "$INFO_ICON" "$BLUE" "Cilium Version: $cilium_version"
fi
else
print_status "$WARN_ICON" "$YELLOW" "Prefix delegation is not enabled in Cilium"
print_status "$INFO_ICON" "$CYAN" "To enable prefix delegation in Cilium:"
print_status "$FIX_ICON" "$CYAN" "1. Edit the cilium-config ConfigMap"
print_status "$FIX_ICON" "$CYAN" "2. Set enable-prefix-delegation: \"true\""
print_status "$FIX_ICON" "$CYAN" "3. Restart Cilium pods to apply changes"
fi
elif [ $aws_node_exists -eq 0 ]; then
cni_type="AWS VPC CNI"
print_status "$WARN_ICON" "$YELLOW" "AWS VPC CNI detected instead of Cilium"
print_status "$INFO_ICON" "$CYAN" "Consider migrating to Cilium for enhanced networking features"
print_status "$INFO_ICON" "$CYAN" "Visit https://docs.cilium.io/en/stable/installation/k8s-install-eks/ for Cilium installation guide"
return 1
else
print_status "$CROSS_MARK" "$RED" "Neither Cilium nor AWS VPC CNI detected"
print_error "No supported CNI found" \
"Install Cilium CNI for advanced networking features" \
"Visit https://docs.cilium.io/en/stable/installation/k8s-install-eks/ for installation guide"
return 1
fi
# Only proceed with ENI checks if Cilium is active with prefix delegation
if [ "$cni_type" = "Cilium" ] && [ "$prefix_delegation_enabled" = true ]; then
echo
print_status "$NET_ICON" "$CYAN" "Checking ENI configuration..."
# Check ENIs
for instance_id in $instances; do
print_status "$NET_ICON" "$CYAN" "Checking ENIs for instance: $instance_id"
eni_ids=$(aws ec2 describe-network-interfaces \
--filters "Name=attachment.instance-id,Values=$instance_id" \
--query "NetworkInterfaces[].NetworkInterfaceId" \
--profile $profile --output text 2>/dev/null)
if [ -z "$eni_ids" ]; then
print_status "$WARN_ICON" "$YELLOW" "No ENIs found for instance: $instance_id"
else
# Print ENI table header
echo
printf "┌─────────────────────────────────────────────────────────────────────────────┐\n"
printf "│ %-20s │ %-30s │ %-20s │\n" "ENI ID" "IP Configuration" "Delegation Status"
printf "├─────────────────────────────────────────────────────────────────────────────┤\n"
for eni_id in $eni_ids; do
((total_enis++))
eni_details=$(aws ec2 describe-network-interfaces \
--network-interface-ids $eni_id --profile $profile 2>/dev/null)
# Get prefixes
local prefixes=$(echo "$eni_details" | jq -r '.NetworkInterfaces[].Ipv4Prefixes[]?.Ipv4Prefix' 2>/dev/null | tr '\n' ' ')
# Format status line
if [ ! -z "$prefixes" ]; then
local prefix_count=$(echo "$prefixes" | wc -w)
((total_prefixes += prefix_count))
printf "│ %-20s │ %-30s │ ${GREEN}%-20s${NC} │\n" \
"$eni_id" \
"Prefixes: $prefix_count" \
"ACTIVE (Cilium)"
else
printf "│ %-20s │ %-30s │ ${YELLOW}%-20s${NC} │\n" \
"$eni_id" \
"No prefixes assigned" \
"INACTIVE"
fi
done
printf "└─────────────────────────────────────────────────────────────────────────────┘\n"
fi
done
# Calculate total available IPs for pods
total_available_ips=$((total_prefixes * 16))
# Print summary
echo
print_status "$INFO_ICON" "$BLUE" "Summary:"
print_status "$INFO_ICON" "$CYAN" "CNI Type: Cilium"
print_status "$INFO_ICON" "$CYAN" "Prefix Delegation: $delegation_status"
print_status "$INFO_ICON" "$CYAN" "Total ENIs: $total_enis"
print_status "$INFO_ICON" "$CYAN" "Total prefixes assigned: $total_prefixes"
print_status "$INFO_ICON" "$CYAN" "Available IPs from prefixes: $total_available_ips"
if [ ! -z "$instance_type" ]; then
print_status "$INFO_ICON" "$CYAN" "Instance type: $instance_type"
fi
echo
print_status "$DEBUG_ICON" "$YELLOW" "Capacity Analysis:"
print_status "$INFO_ICON" "$CYAN" "Using prefix delegation mode (Managed by Cilium)"
print_status "$INFO_ICON" "$CYAN" "Each prefix (/28) provides 16 pod IPs"
print_status "$INFO_ICON" "$CYAN" "Total available pod IPs: $total_available_ips"
fi
}
clear
print_header "AWS EKS Cilium Prefix Delegation Check"
print_status "$INFO_ICON" "$BLUE" "Starting checks..."
check_requirements
fetch_instances
check_enis
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment