-
-
Save oguzhancoskun/aaf0484f2c426c1fd15b4a9fb85bf3ac to your computer and use it in GitHub Desktop.
AWS EKS Cilium Prefix Delegation Checker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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