import boto3
from botocore.exceptions import ClientError
import time

# A script to create an EKS cluster with a managed node group using Boto3.
# This script assumes a VPC with public and private subnets, and IAM roles
# for the EKS cluster and node group are pre-existing or created separately.
# For a full end-to-end setup including VPC and IAM, consider using eksctl or Terraform.

# --- Configuration ---
REGION = "us-east-1"
CLUSTER_NAME = "my-boto3-eks-cluster"
NODE_GROUP_NAME = "my-boto3-managed-nodegroup"
KUBERNETES_VERSION = "1.28" # Specify a supported Kubernetes version

# !!! IMPORTANT: Replace with your actual VPC, Subnet, and IAM Role ARNs !!!
# These should be created beforehand or dynamically fetched.
VPC_ID = "vpc-0abcdef1234567890" # Example: Replace with your VPC ID
PUBLIC_SUBNET_IDS = ["subnet-0abcdef1234567890", "subnet-0fedcba9876543210"] # Example: Replace with your public subnet IDs
PRIVATE_SUBNET_IDS = ["subnet-0123456789abcdef0", "subnet-0abcdef0123456789"] # Example: Replace with your private subnet IDs
EKS_CLUSTER_ROLE_ARN = "arn:aws:iam::123456789012:role/my-eks-cluster-role" # Example: Replace with your EKS Cluster IAM Role ARN
EKS_NODEGROUP_ROLE_ARN = "arn:aws:iam::123456789012:role/my-eks-nodegroup-role" # Example: Replace with your EKS Node Group IAM Role ARN

eks_client = boto3.client('eks', region_name=REGION)

def create_eks_cluster():
    """Creates an EKS cluster."""
    print(f"--- Creating EKS Cluster: {CLUSTER_NAME} ---")
    try:
        response = eks_client.create_cluster(
            name=CLUSTER_NAME,
            version=KUBERNETES_VERSION,
            roleArn=EKS_CLUSTER_ROLE_ARN,
            resourcesVpcConfig={
                'subnetIds': PUBLIC_SUBNET_IDS + PRIVATE_SUBNET_IDS,
                'endpointPublicAccess': True,
                'endpointPrivateAccess': False
            },
            tags={'Name': CLUSTER_NAME}
        )
        print(f"EKS Cluster '{CLUSTER_NAME}' created. Waiting for it to become active (this can take 10-15 minutes)...")
        eks_client.get_waiter('cluster_active').wait(name=CLUSTER_NAME)
        print("EKS Cluster is active.")
        return response['cluster']['arn']
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceInUseException':
            print(f"EKS Cluster '{CLUSTER_NAME}' already exists. Skipping creation.")
            return eks_client.describe_cluster(name=CLUSTER_NAME)['cluster']['arn']
        else:
            print(f"Error creating EKS cluster: {e}")
            raise

def create_managed_node_group():
    """Creates a managed node group for the EKS cluster."""
    print(f"\n--- Creating Managed Node Group: {NODE_GROUP_NAME} ---")
    try:
        response = eks_client.create_node_group(
            clusterName=CLUSTER_NAME,
            nodeGroupName=NODE_GROUP_NAME,
            scalingConfig={'minSize': 1, 'maxSize': 3, 'desiredSize': 2},
            instanceTypes=['t3.medium'],
            amiType='AL2_x86_64',
            nodeRole=EKS_NODEGROUP_ROLE_ARN,
            subnetIds=PRIVATE_SUBNET_IDS, # Typically private subnets for worker nodes
            tags={'Name': NODE_GROUP_NAME}
        )
        print(f"Managed Node Group '{NODE_GROUP_NAME}' created. Waiting for it to become active (this can take a few minutes)...")
        eks_client.get_waiter('nodegroup_active').wait(clusterName=CLUSTER_NAME, nodeGroupName=NODE_GROUP_NAME)
        print("Managed Node Group is active.")
        return response['nodeGroup']['nodeGroupArn']
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceInUseException':
            print(f"Managed Node Group '{NODE_GROUP_NAME}' already exists. Skipping creation.")
            return eks_client.describe_node_group(clusterName=CLUSTER_NAME, nodeGroupName=NODE_GROUP_NAME)['nodeGroup']['nodeGroupArn']
        else:
            print(f"Error creating managed node group: {e}")
            raise

def cleanup_resources():
    """Cleans up all created resources."""
    print(f"\n--- Cleaning up resources ---")

    # Delete Managed Node Group
    print(f"Deleting Managed Node Group '{NODE_GROUP_NAME}'...")
    try:
        eks_client.delete_node_group(clusterName=CLUSTER_NAME, nodeGroupName=NODE_GROUP_NAME)
        eks_client.get_waiter('nodegroup_deleted').wait(clusterName=CLUSTER_NAME, nodeGroupName=NODE_GROUP_NAME)
        print("Managed Node Group deleted.")
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceNotFoundException':
            print(f"Managed Node Group '{NODE_GROUP_NAME}' not found, skipping deletion.")
        else:
            print(f"Error deleting managed node group: {e}")

    # Delete EKS Cluster
    print(f"Deleting EKS Cluster '{CLUSTER_NAME}'...")
    try:
        eks_client.delete_cluster(name=CLUSTER_NAME)
        eks_client.get_waiter('cluster_deleted').wait(name=CLUSTER_NAME)
        print("EKS Cluster deleted.")
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceNotFoundException':
            print(f"EKS Cluster '{CLUSTER_NAME}' not found, skipping deletion.")
        else:
            print(f"Error deleting EKS cluster: {e}")

def main():
    try:
        cluster_arn = create_eks_cluster()
        nodegroup_arn = create_managed_node_group()

        cluster_info = eks_client.describe_cluster(name=CLUSTER_NAME)['cluster']
        cluster_endpoint = cluster_info['endpoint']

        print("\n--- EKS Cluster Setup Complete! ---")
        print(f"Cluster Name: {CLUSTER_NAME}")
        print(f"Cluster Endpoint: {cluster_endpoint}")
        print(f"Managed Node Group: {NODE_GROUP_NAME}")
        print(f"To connect using kubectl, run: aws eks update-kubeconfig --name {CLUSTER_NAME} --region {REGION}")

        input("Press Enter to delete the EKS cluster and clean up resources...")

    except ClientError as e:
        print(f"An AWS client error occurred: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
    finally:
        cleanup_resources()
        print("\n--- EKS cluster demonstration and cleanup complete ---")

if __name__ == "__main__":
    main()
