k8s-helper-cli 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/PKG-INFO +1 -1
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/pyproject.toml +1 -1
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper/__init__.py +1 -1
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper/cli.py +233 -7
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper/core.py +349 -7
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/PKG-INFO +1 -1
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/LICENSE +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/README.md +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/setup.cfg +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper/config.py +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper/utils.py +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/SOURCES.txt +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/dependency_links.txt +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/entry_points.txt +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/requires.txt +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/top_level.txt +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/tests/test_core.py +0 -0
- {k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/tests/test_integration.py +0 -0
@@ -664,7 +664,10 @@ def create_eks_cluster(
|
|
664
664
|
min_size: int = typer.Option(1, "--min-size", help="Minimum number of nodes"),
|
665
665
|
max_size: int = typer.Option(3, "--max-size", help="Maximum number of nodes"),
|
666
666
|
desired_size: int = typer.Option(2, "--desired-size", help="Desired number of nodes"),
|
667
|
-
wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for cluster to be ready")
|
667
|
+
wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for cluster to be ready"),
|
668
|
+
create_nodegroup: bool = typer.Option(True, "--create-nodegroup/--no-nodegroup", help="Create node group automatically"),
|
669
|
+
ami_type: str = typer.Option("AL2_x86_64", "--ami-type", help="AMI type for nodes"),
|
670
|
+
capacity_type: str = typer.Option("ON_DEMAND", "--capacity-type", help="Capacity type: ON_DEMAND or SPOT")
|
668
671
|
):
|
669
672
|
"""Create an AWS EKS cluster"""
|
670
673
|
if not validate_name(name):
|
@@ -690,15 +693,22 @@ def create_eks_cluster(
|
|
690
693
|
console.print(f"🎯 Version: {version}")
|
691
694
|
console.print(f"💻 Instance types: {instance_type_list}")
|
692
695
|
console.print(f"📊 Scaling: {min_size}-{max_size} nodes (desired: {desired_size})")
|
696
|
+
console.print(f"🛠️ AMI Type: {ami_type}")
|
697
|
+
console.print(f"⚡ Capacity Type: {capacity_type}")
|
693
698
|
|
694
699
|
# Show what will be created
|
695
|
-
console.print("\n🔧 EKS
|
700
|
+
console.print("\n🔧 EKS Resources to create:")
|
696
701
|
console.print(" • IAM roles for cluster and node groups")
|
697
702
|
console.print(" • VPC subnets in at least 2 availability zones")
|
698
703
|
console.print(" • Security groups for cluster communication")
|
699
704
|
console.print(" • EKS cluster control plane")
|
700
|
-
if
|
701
|
-
|
705
|
+
if create_nodegroup:
|
706
|
+
if node_group:
|
707
|
+
console.print(f" • Managed node group: {node_group}")
|
708
|
+
else:
|
709
|
+
console.print(f" • Managed node group: {name}-nodegroup")
|
710
|
+
else:
|
711
|
+
console.print(" • ⚠️ No node group (cluster will have no worker nodes)")
|
702
712
|
|
703
713
|
with console.status("Creating EKS cluster and required resources..."):
|
704
714
|
cluster_info = eks_client.create_cluster(
|
@@ -706,7 +716,11 @@ def create_eks_cluster(
|
|
706
716
|
version=version,
|
707
717
|
node_group_name=node_group,
|
708
718
|
instance_types=instance_type_list,
|
709
|
-
scaling_config=scaling_config
|
719
|
+
scaling_config=scaling_config,
|
720
|
+
ami_type=ami_type,
|
721
|
+
capacity_type=capacity_type,
|
722
|
+
create_nodegroup=create_nodegroup,
|
723
|
+
wait_for_cluster=wait
|
710
724
|
)
|
711
725
|
|
712
726
|
console.print(f"✅ EKS cluster creation initiated")
|
@@ -716,6 +730,22 @@ def create_eks_cluster(
|
|
716
730
|
if 'subnets' in cluster_info:
|
717
731
|
console.print(f"🌐 Subnets: {cluster_info['subnets']}")
|
718
732
|
|
733
|
+
# Show node group information
|
734
|
+
if create_nodegroup:
|
735
|
+
if 'nodegroup_info' in cluster_info:
|
736
|
+
nodegroup_info = cluster_info['nodegroup_info']
|
737
|
+
console.print(f"\n✅ Node group creation initiated")
|
738
|
+
console.print(f"📋 Node group name: {nodegroup_info['nodegroup_name']}")
|
739
|
+
console.print(f"📋 Node group ARN: {nodegroup_info['nodegroup_arn']}")
|
740
|
+
console.print(f"💻 Instance types: {nodegroup_info['instance_types']}")
|
741
|
+
console.print(f"📊 Scaling config: {nodegroup_info['scaling_config']}")
|
742
|
+
elif 'nodegroup_error' in cluster_info:
|
743
|
+
console.print(f"\n⚠️ Node group creation failed: {cluster_info['nodegroup_error']}")
|
744
|
+
console.print(f"💡 You can create it manually later using: k8s-helper create-nodegroup {name}")
|
745
|
+
elif 'node_group_name' in cluster_info:
|
746
|
+
console.print(f"\n📋 Node group will be created: {cluster_info['node_group_name']}")
|
747
|
+
console.print(f"💡 Create it after cluster is active: k8s-helper create-nodegroup {name}")
|
748
|
+
|
719
749
|
if wait:
|
720
750
|
console.print("⏳ Waiting for cluster to become active...")
|
721
751
|
with console.status("Waiting for cluster to be ready..."):
|
@@ -726,11 +756,53 @@ def create_eks_cluster(
|
|
726
756
|
status = eks_client.get_cluster_status(name)
|
727
757
|
console.print(f"🔗 Endpoint: {status['endpoint']}")
|
728
758
|
|
759
|
+
# If node group was created, wait for it too
|
760
|
+
if create_nodegroup and 'nodegroup_info' in cluster_info:
|
761
|
+
nodegroup_name = cluster_info['nodegroup_info']['nodegroup_name']
|
762
|
+
console.print(f"⏳ Waiting for node group {nodegroup_name} to become active...")
|
763
|
+
with console.status("Waiting for node group to be ready..."):
|
764
|
+
if eks_client.wait_for_nodegroup_active(name, nodegroup_name):
|
765
|
+
console.print("✅ Node group is now active!")
|
766
|
+
console.print("🎉 Cluster is ready with worker nodes!")
|
767
|
+
else:
|
768
|
+
console.print("❌ Timeout waiting for node group to become active")
|
769
|
+
elif create_nodegroup and not wait:
|
770
|
+
# Create node group now that cluster is active
|
771
|
+
nodegroup_name = cluster_info.get('node_group_name') or f"{name}-nodegroup"
|
772
|
+
console.print(f"🔧 Creating node group: {nodegroup_name}")
|
773
|
+
try:
|
774
|
+
with console.status("Creating node group..."):
|
775
|
+
nodegroup_info = eks_client.create_nodegroup(
|
776
|
+
cluster_name=name,
|
777
|
+
nodegroup_name=nodegroup_name,
|
778
|
+
instance_types=instance_type_list,
|
779
|
+
ami_type=ami_type,
|
780
|
+
capacity_type=capacity_type,
|
781
|
+
scaling_config=scaling_config
|
782
|
+
)
|
783
|
+
console.print(f"✅ Node group creation initiated: {nodegroup_name}")
|
784
|
+
console.print(f"📋 Node group ARN: {nodegroup_info['nodegroup_arn']}")
|
785
|
+
|
786
|
+
console.print(f"⏳ Waiting for node group to become active...")
|
787
|
+
if eks_client.wait_for_nodegroup_active(name, nodegroup_name):
|
788
|
+
console.print("✅ Node group is now active!")
|
789
|
+
console.print("🎉 Cluster is ready with worker nodes!")
|
790
|
+
else:
|
791
|
+
console.print("❌ Timeout waiting for node group to become active")
|
792
|
+
except Exception as e:
|
793
|
+
console.print(f"❌ Failed to create node group: {e}")
|
794
|
+
|
729
795
|
# Show next steps
|
730
796
|
console.print(f"\n🚀 Next steps:")
|
731
797
|
console.print(f" 1. Configure kubectl: aws eks update-kubeconfig --name {name} --region {region}")
|
732
|
-
|
733
|
-
|
798
|
+
if create_nodegroup:
|
799
|
+
console.print(f" 2. Verify nodes: kubectl get nodes")
|
800
|
+
console.print(f" 3. Verify connection: kubectl get svc")
|
801
|
+
console.print(f" 4. Deploy applications: k8s-helper apply <app-name> <image>")
|
802
|
+
else:
|
803
|
+
console.print(f" 2. Create node group: k8s-helper create-nodegroup {name}")
|
804
|
+
console.print(f" 3. Verify connection: kubectl get svc")
|
805
|
+
console.print(f" 4. Deploy applications: k8s-helper apply <app-name> <image>")
|
734
806
|
else:
|
735
807
|
console.print("❌ Timeout waiting for cluster to become active")
|
736
808
|
else:
|
@@ -758,7 +830,161 @@ def create_eks_cluster(
|
|
758
830
|
console.print(" • Ensure you have a default VPC or create one")
|
759
831
|
console.print(" • Verify subnet CIDR ranges don't overlap")
|
760
832
|
|
833
|
+
@app.command()
|
834
|
+
def create_nodegroup(
|
835
|
+
cluster_name: str = typer.Argument(..., help="EKS cluster name"),
|
836
|
+
nodegroup_name: str = typer.Argument(..., help="Node group name"),
|
837
|
+
region: str = typer.Option("us-west-2", "--region", "-r", help="AWS region"),
|
838
|
+
instance_types: str = typer.Option("t3.medium", "--instance-types", help="EC2 instance types (comma-separated)"),
|
839
|
+
min_size: int = typer.Option(1, "--min-size", help="Minimum number of nodes"),
|
840
|
+
max_size: int = typer.Option(3, "--max-size", help="Maximum number of nodes"),
|
841
|
+
desired_size: int = typer.Option(2, "--desired-size", help="Desired number of nodes"),
|
842
|
+
ami_type: str = typer.Option("AL2_x86_64", "--ami-type", help="AMI type for nodes"),
|
843
|
+
capacity_type: str = typer.Option("ON_DEMAND", "--capacity-type", help="Capacity type: ON_DEMAND or SPOT"),
|
844
|
+
wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for node group to be ready")
|
845
|
+
):
|
846
|
+
"""Create an EKS managed node group"""
|
847
|
+
if not validate_name(cluster_name):
|
848
|
+
console.print(f"❌ Invalid cluster name: {cluster_name}")
|
849
|
+
return
|
850
|
+
|
851
|
+
if not validate_name(nodegroup_name):
|
852
|
+
console.print(f"❌ Invalid node group name: {nodegroup_name}")
|
853
|
+
return
|
854
|
+
|
855
|
+
try:
|
856
|
+
from .core import EKSClient
|
857
|
+
|
858
|
+
eks_client = EKSClient(region=region)
|
859
|
+
|
860
|
+
# Parse instance types
|
861
|
+
instance_type_list = [t.strip() for t in instance_types.split(",")]
|
862
|
+
|
863
|
+
scaling_config = {
|
864
|
+
"minSize": min_size,
|
865
|
+
"maxSize": max_size,
|
866
|
+
"desiredSize": desired_size
|
867
|
+
}
|
868
|
+
|
869
|
+
console.print(f"🚀 Creating node group: {nodegroup_name}")
|
870
|
+
console.print(f"📋 For cluster: {cluster_name}")
|
871
|
+
console.print(f"📍 Region: {region}")
|
872
|
+
console.print(f"💻 Instance types: {instance_type_list}")
|
873
|
+
console.print(f"📊 Scaling: {min_size}-{max_size} nodes (desired: {desired_size})")
|
874
|
+
console.print(f"🛠️ AMI Type: {ami_type}")
|
875
|
+
console.print(f"⚡ Capacity Type: {capacity_type}")
|
876
|
+
|
877
|
+
# Check if cluster exists and is active
|
878
|
+
try:
|
879
|
+
cluster_status = eks_client.get_cluster_status(cluster_name)
|
880
|
+
if cluster_status['status'] != 'ACTIVE':
|
881
|
+
console.print(f"❌ Cluster {cluster_name} is not active (status: {cluster_status['status']})")
|
882
|
+
console.print("💡 Wait for cluster to become active before creating node group")
|
883
|
+
return
|
884
|
+
except Exception as e:
|
885
|
+
console.print(f"❌ Cluster {cluster_name} not found: {e}")
|
886
|
+
return
|
887
|
+
|
888
|
+
with console.status("Creating node group..."):
|
889
|
+
nodegroup_info = eks_client.create_nodegroup(
|
890
|
+
cluster_name=cluster_name,
|
891
|
+
nodegroup_name=nodegroup_name,
|
892
|
+
instance_types=instance_type_list,
|
893
|
+
ami_type=ami_type,
|
894
|
+
capacity_type=capacity_type,
|
895
|
+
scaling_config=scaling_config
|
896
|
+
)
|
897
|
+
|
898
|
+
console.print(f"✅ Node group creation initiated")
|
899
|
+
console.print(f"📋 Node group ARN: {nodegroup_info['nodegroup_arn']}")
|
900
|
+
console.print(f"🕐 Created at: {nodegroup_info['created_at']}")
|
901
|
+
console.print(f"💻 Instance types: {nodegroup_info['instance_types']}")
|
902
|
+
console.print(f"📊 Scaling config: {nodegroup_info['scaling_config']}")
|
903
|
+
|
904
|
+
if wait:
|
905
|
+
console.print("⏳ Waiting for node group to become active...")
|
906
|
+
with console.status("Waiting for node group to be ready..."):
|
907
|
+
if eks_client.wait_for_nodegroup_active(cluster_name, nodegroup_name):
|
908
|
+
console.print("✅ Node group is now active!")
|
909
|
+
console.print("🎉 You can now deploy workloads!")
|
910
|
+
|
911
|
+
# Show next steps
|
912
|
+
console.print(f"\n🚀 Next steps:")
|
913
|
+
console.print(f" 1. Verify nodes: kubectl get nodes")
|
914
|
+
console.print(f" 2. Deploy applications: k8s-helper apply <app-name> <image>")
|
915
|
+
else:
|
916
|
+
console.print("❌ Timeout waiting for node group to become active")
|
917
|
+
else:
|
918
|
+
console.print(f"💡 Use 'kubectl get nodes' to check when nodes are ready")
|
919
|
+
|
920
|
+
except Exception as e:
|
921
|
+
error_message = str(e)
|
922
|
+
console.print(f"❌ Failed to create node group: {error_message}")
|
923
|
+
|
924
|
+
# Provide helpful guidance based on error type
|
925
|
+
if "Node group role" in error_message:
|
926
|
+
console.print("\n🛠️ Troubleshooting:")
|
927
|
+
console.print(" • The tool will automatically create required IAM roles")
|
928
|
+
console.print(" • Ensure you have IAM permissions to create roles")
|
929
|
+
console.print(" • Check AWS IAM console for existing roles")
|
930
|
+
elif "subnets" in error_message:
|
931
|
+
console.print("\n🛠️ Troubleshooting:")
|
932
|
+
console.print(" • Node groups require valid subnets")
|
933
|
+
console.print(" • Ensure cluster subnets are properly configured")
|
934
|
+
console.print(" • Check VPC and subnet configuration")
|
761
935
|
|
936
|
+
@app.command()
|
937
|
+
def list_nodegroups(
|
938
|
+
cluster_name: str = typer.Argument(..., help="EKS cluster name"),
|
939
|
+
region: str = typer.Option("us-west-2", "--region", "-r", help="AWS region"),
|
940
|
+
output: str = output_option
|
941
|
+
):
|
942
|
+
"""List node groups for an EKS cluster"""
|
943
|
+
if not validate_name(cluster_name):
|
944
|
+
console.print(f"❌ Invalid cluster name: {cluster_name}")
|
945
|
+
return
|
946
|
+
|
947
|
+
try:
|
948
|
+
from .core import EKSClient
|
949
|
+
|
950
|
+
eks_client = EKSClient(region=region)
|
951
|
+
|
952
|
+
with console.status("Fetching node groups..."):
|
953
|
+
nodegroups = eks_client.list_nodegroups(cluster_name)
|
954
|
+
|
955
|
+
if not nodegroups:
|
956
|
+
console.print(f"📋 No node groups found for cluster: {cluster_name}")
|
957
|
+
console.print(f"💡 Create one with: k8s-helper create-nodegroup {cluster_name} <nodegroup-name>")
|
958
|
+
return
|
959
|
+
|
960
|
+
if output == "table":
|
961
|
+
table = Table(title=f"Node Groups for {cluster_name}")
|
962
|
+
table.add_column("Name", style="cyan")
|
963
|
+
table.add_column("Status", style="green")
|
964
|
+
table.add_column("Instance Types", style="blue")
|
965
|
+
table.add_column("Capacity Type", style="yellow")
|
966
|
+
table.add_column("Scaling Config", style="magenta")
|
967
|
+
table.add_column("Created", style="white")
|
968
|
+
|
969
|
+
for ng in nodegroups:
|
970
|
+
scaling = f"{ng['scaling_config']['minSize']}-{ng['scaling_config']['maxSize']} (desired: {ng['scaling_config']['desiredSize']})"
|
971
|
+
table.add_row(
|
972
|
+
ng['name'],
|
973
|
+
ng['status'],
|
974
|
+
', '.join(ng['instance_types']),
|
975
|
+
ng['capacity_type'],
|
976
|
+
scaling,
|
977
|
+
format_age(ng['created_at'])
|
978
|
+
)
|
979
|
+
|
980
|
+
console.print(table)
|
981
|
+
elif output == "json":
|
982
|
+
console.print(format_json_output(nodegroups))
|
983
|
+
elif output == "yaml":
|
984
|
+
console.print(format_yaml_output(nodegroups))
|
985
|
+
|
986
|
+
except Exception as e:
|
987
|
+
console.print(f"❌ Failed to list node groups: {e}")
|
762
988
|
# ======================
|
763
989
|
# SECRET COMMANDS
|
764
990
|
# ======================
|
@@ -30,7 +30,8 @@ class EKSClient:
|
|
30
30
|
subnets: List[str] = None, security_groups: List[str] = None,
|
31
31
|
role_arn: str = None, node_group_name: str = None,
|
32
32
|
instance_types: List[str] = None, ami_type: str = "AL2_x86_64",
|
33
|
-
capacity_type: str = "ON_DEMAND", scaling_config: Dict = None
|
33
|
+
capacity_type: str = "ON_DEMAND", scaling_config: Dict = None,
|
34
|
+
create_nodegroup: bool = True, wait_for_cluster: bool = False) -> Dict:
|
34
35
|
"""Create an EKS cluster
|
35
36
|
|
36
37
|
Args:
|
@@ -44,6 +45,8 @@ class EKSClient:
|
|
44
45
|
ami_type: AMI type for nodes
|
45
46
|
capacity_type: Capacity type (ON_DEMAND or SPOT)
|
46
47
|
scaling_config: Scaling configuration for node group
|
48
|
+
create_nodegroup: Whether to create a node group automatically
|
49
|
+
wait_for_cluster: Whether to wait for cluster to be active before returning
|
47
50
|
|
48
51
|
Returns:
|
49
52
|
Dict containing cluster information
|
@@ -74,12 +77,16 @@ class EKSClient:
|
|
74
77
|
resourcesVpcConfig={
|
75
78
|
'subnetIds': subnets,
|
76
79
|
'securityGroupIds': security_groups or [],
|
77
|
-
'
|
78
|
-
'
|
80
|
+
'endpointPublicAccess': True,
|
81
|
+
'endpointPrivateAccess': True
|
79
82
|
},
|
80
83
|
logging={
|
81
|
-
'
|
82
|
-
|
84
|
+
'clusterLogging': [
|
85
|
+
{
|
86
|
+
'types': ['api', 'audit', 'authenticator', 'controllerManager', 'scheduler'],
|
87
|
+
'enabled': True
|
88
|
+
}
|
89
|
+
]
|
83
90
|
}
|
84
91
|
)
|
85
92
|
|
@@ -94,11 +101,34 @@ class EKSClient:
|
|
94
101
|
'created_at': cluster_response['cluster']['createdAt']
|
95
102
|
}
|
96
103
|
|
97
|
-
#
|
98
|
-
if
|
104
|
+
# Create node group if requested
|
105
|
+
if create_nodegroup:
|
106
|
+
if node_group_name is None:
|
107
|
+
node_group_name = f"{cluster_name}-nodegroup"
|
108
|
+
|
99
109
|
cluster_info['node_group_name'] = node_group_name
|
100
110
|
cluster_info['instance_types'] = instance_types
|
101
111
|
cluster_info['scaling_config'] = scaling_config
|
112
|
+
cluster_info['create_nodegroup'] = True
|
113
|
+
|
114
|
+
# If wait_for_cluster is True, create the node group after cluster is active
|
115
|
+
if wait_for_cluster:
|
116
|
+
if self.wait_for_cluster_active(cluster_name):
|
117
|
+
try:
|
118
|
+
nodegroup_info = self.create_nodegroup(
|
119
|
+
cluster_name=cluster_name,
|
120
|
+
nodegroup_name=node_group_name,
|
121
|
+
instance_types=instance_types,
|
122
|
+
ami_type=ami_type,
|
123
|
+
capacity_type=capacity_type,
|
124
|
+
scaling_config=scaling_config,
|
125
|
+
subnets=subnets
|
126
|
+
)
|
127
|
+
cluster_info['nodegroup_info'] = nodegroup_info
|
128
|
+
except Exception as e:
|
129
|
+
cluster_info['nodegroup_error'] = str(e)
|
130
|
+
else:
|
131
|
+
cluster_info['nodegroup_error'] = "Cluster creation timed out"
|
102
132
|
|
103
133
|
return cluster_info
|
104
134
|
|
@@ -256,6 +286,318 @@ class EKSClient:
|
|
256
286
|
else:
|
257
287
|
raise Exception(f"Failed to create or get cluster role: {e}")
|
258
288
|
|
289
|
+
def _create_or_get_nodegroup_role(self) -> str:
|
290
|
+
"""Create or get IAM role for EKS node group"""
|
291
|
+
role_name = "eks-nodegroup-role"
|
292
|
+
|
293
|
+
try:
|
294
|
+
# Check if role exists
|
295
|
+
response = self.iam_client.get_role(RoleName=role_name)
|
296
|
+
return response['Role']['Arn']
|
297
|
+
|
298
|
+
except ClientError as e:
|
299
|
+
if e.response['Error']['Code'] == 'NoSuchEntity':
|
300
|
+
# Create the role
|
301
|
+
trust_policy = {
|
302
|
+
"Version": "2012-10-17",
|
303
|
+
"Statement": [
|
304
|
+
{
|
305
|
+
"Effect": "Allow",
|
306
|
+
"Principal": {
|
307
|
+
"Service": "ec2.amazonaws.com"
|
308
|
+
},
|
309
|
+
"Action": "sts:AssumeRole"
|
310
|
+
}
|
311
|
+
]
|
312
|
+
}
|
313
|
+
|
314
|
+
response = self.iam_client.create_role(
|
315
|
+
RoleName=role_name,
|
316
|
+
AssumeRolePolicyDocument=json.dumps(trust_policy),
|
317
|
+
Description="EKS node group role created by k8s-helper"
|
318
|
+
)
|
319
|
+
|
320
|
+
# Attach required policies for node group
|
321
|
+
policies = [
|
322
|
+
"arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
|
323
|
+
"arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
|
324
|
+
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
|
325
|
+
]
|
326
|
+
|
327
|
+
for policy in policies:
|
328
|
+
self.iam_client.attach_role_policy(
|
329
|
+
RoleName=role_name,
|
330
|
+
PolicyArn=policy
|
331
|
+
)
|
332
|
+
|
333
|
+
return response['Role']['Arn']
|
334
|
+
else:
|
335
|
+
raise Exception(f"Failed to create or get node group role: {e}")
|
336
|
+
|
337
|
+
def create_nodegroup(self, cluster_name: str, nodegroup_name: str,
|
338
|
+
instance_types: List[str] = None, ami_type: str = "AL2_x86_64",
|
339
|
+
capacity_type: str = "ON_DEMAND", scaling_config: Dict = None,
|
340
|
+
subnets: List[str] = None, node_role_arn: str = None) -> Dict:
|
341
|
+
"""Create an EKS managed node group
|
342
|
+
|
343
|
+
Args:
|
344
|
+
cluster_name: Name of the EKS cluster
|
345
|
+
nodegroup_name: Name of the node group
|
346
|
+
instance_types: List of EC2 instance types
|
347
|
+
ami_type: AMI type for nodes
|
348
|
+
capacity_type: Capacity type (ON_DEMAND or SPOT)
|
349
|
+
scaling_config: Scaling configuration
|
350
|
+
subnets: List of subnet IDs
|
351
|
+
node_role_arn: IAM role ARN for nodes
|
352
|
+
|
353
|
+
Returns:
|
354
|
+
Dict containing node group information
|
355
|
+
"""
|
356
|
+
try:
|
357
|
+
# Use defaults if not provided
|
358
|
+
if instance_types is None:
|
359
|
+
instance_types = ["t3.medium"]
|
360
|
+
|
361
|
+
if scaling_config is None:
|
362
|
+
scaling_config = {
|
363
|
+
"minSize": 1,
|
364
|
+
"maxSize": 3,
|
365
|
+
"desiredSize": 2
|
366
|
+
}
|
367
|
+
|
368
|
+
if node_role_arn is None:
|
369
|
+
node_role_arn = self._create_or_get_nodegroup_role()
|
370
|
+
|
371
|
+
if subnets is None:
|
372
|
+
# Get cluster's subnets
|
373
|
+
cluster_info = self.get_cluster_status(cluster_name)
|
374
|
+
cluster_details = self.eks_client.describe_cluster(name=cluster_name)
|
375
|
+
subnets = cluster_details['cluster']['resourcesVpcConfig']['subnetIds']
|
376
|
+
|
377
|
+
# Create the node group
|
378
|
+
response = self.eks_client.create_nodegroup(
|
379
|
+
clusterName=cluster_name,
|
380
|
+
nodegroupName=nodegroup_name,
|
381
|
+
scalingConfig=scaling_config,
|
382
|
+
instanceTypes=instance_types,
|
383
|
+
amiType=ami_type,
|
384
|
+
capacityType=capacity_type,
|
385
|
+
nodeRole=node_role_arn,
|
386
|
+
subnets=subnets,
|
387
|
+
remoteAccess={
|
388
|
+
'ec2SshKey': None # SSH key is optional
|
389
|
+
}
|
390
|
+
)
|
391
|
+
|
392
|
+
return {
|
393
|
+
'nodegroup_name': nodegroup_name,
|
394
|
+
'cluster_name': cluster_name,
|
395
|
+
'status': 'CREATING',
|
396
|
+
'nodegroup_arn': response['nodegroup']['nodegroupArn'],
|
397
|
+
'instance_types': instance_types,
|
398
|
+
'ami_type': ami_type,
|
399
|
+
'capacity_type': capacity_type,
|
400
|
+
'scaling_config': scaling_config,
|
401
|
+
'created_at': response['nodegroup']['createdAt'],
|
402
|
+
'node_role_arn': node_role_arn
|
403
|
+
}
|
404
|
+
|
405
|
+
except ClientError as e:
|
406
|
+
raise Exception(f"Failed to create node group: {e}")
|
407
|
+
|
408
|
+
def get_nodegroup_status(self, cluster_name: str, nodegroup_name: str) -> Dict:
|
409
|
+
"""Get EKS node group status"""
|
410
|
+
try:
|
411
|
+
response = self.eks_client.describe_nodegroup(
|
412
|
+
clusterName=cluster_name,
|
413
|
+
nodegroupName=nodegroup_name
|
414
|
+
)
|
415
|
+
nodegroup = response['nodegroup']
|
416
|
+
|
417
|
+
return {
|
418
|
+
'name': nodegroup['nodegroupName'],
|
419
|
+
'cluster_name': cluster_name,
|
420
|
+
'status': nodegroup['status'],
|
421
|
+
'instance_types': nodegroup['instanceTypes'],
|
422
|
+
'ami_type': nodegroup['amiType'],
|
423
|
+
'capacity_type': nodegroup['capacityType'],
|
424
|
+
'scaling_config': nodegroup['scalingConfig'],
|
425
|
+
'created_at': nodegroup['createdAt'],
|
426
|
+
'arn': nodegroup['nodegroupArn']
|
427
|
+
}
|
428
|
+
|
429
|
+
except ClientError as e:
|
430
|
+
raise Exception(f"Failed to get node group status: {e}")
|
431
|
+
|
432
|
+
def wait_for_nodegroup_active(self, cluster_name: str, nodegroup_name: str, timeout: int = 1200) -> bool:
|
433
|
+
"""Wait for EKS node group to become active"""
|
434
|
+
import time
|
435
|
+
start_time = time.time()
|
436
|
+
|
437
|
+
while time.time() - start_time < timeout:
|
438
|
+
try:
|
439
|
+
status = self.get_nodegroup_status(cluster_name, nodegroup_name)
|
440
|
+
if status['status'] == 'ACTIVE':
|
441
|
+
return True
|
442
|
+
elif status['status'] in ['CREATE_FAILED', 'DEGRADED']:
|
443
|
+
raise Exception(f"Node group creation failed with status: {status['status']}")
|
444
|
+
|
445
|
+
time.sleep(30) # Check every 30 seconds
|
446
|
+
|
447
|
+
except Exception as e:
|
448
|
+
raise Exception(f"Error waiting for node group: {e}")
|
449
|
+
|
450
|
+
return False
|
451
|
+
|
452
|
+
def list_nodegroups(self, cluster_name: str) -> List[Dict]:
|
453
|
+
"""List all node groups for a cluster"""
|
454
|
+
try:
|
455
|
+
response = self.eks_client.list_nodegroups(clusterName=cluster_name)
|
456
|
+
nodegroups = []
|
457
|
+
|
458
|
+
for nodegroup_name in response['nodegroups']:
|
459
|
+
try:
|
460
|
+
nodegroup_info = self.get_nodegroup_status(cluster_name, nodegroup_name)
|
461
|
+
nodegroups.append(nodegroup_info)
|
462
|
+
except Exception as e:
|
463
|
+
# Skip node groups that can't be described
|
464
|
+
continue
|
465
|
+
|
466
|
+
return nodegroups
|
467
|
+
|
468
|
+
except ClientError as e:
|
469
|
+
raise Exception(f"Failed to list node groups: {e}")
|
470
|
+
|
471
|
+
def create_nodegroup_with_instance_profile(self, cluster_name: str, nodegroup_name: str,
|
472
|
+
instance_types: List[str] = None, ami_type: str = "AL2_x86_64",
|
473
|
+
capacity_type: str = "ON_DEMAND", scaling_config: Dict = None,
|
474
|
+
subnets: List[str] = None, node_role_arn: str = None,
|
475
|
+
instance_profile: str = None) -> Dict:
|
476
|
+
"""Create an EKS managed node group with a specific instance profile
|
477
|
+
|
478
|
+
Args:
|
479
|
+
cluster_name: Name of the EKS cluster
|
480
|
+
nodegroup_name: Name of the node group
|
481
|
+
instance_types: List of EC2 instance types
|
482
|
+
ami_type: AMI type for nodes
|
483
|
+
capacity_type: Capacity type (ON_DEMAND or SPOT)
|
484
|
+
scaling_config: Scaling configuration
|
485
|
+
subnets: List of subnet IDs
|
486
|
+
node_role_arn: IAM role ARN for nodes
|
487
|
+
instance_profile: EC2 instance profile name
|
488
|
+
|
489
|
+
Returns:
|
490
|
+
Dict containing node group information
|
491
|
+
"""
|
492
|
+
try:
|
493
|
+
# Use defaults if not provided
|
494
|
+
if instance_types is None:
|
495
|
+
instance_types = ["t3.medium"]
|
496
|
+
|
497
|
+
if scaling_config is None:
|
498
|
+
scaling_config = {
|
499
|
+
"minSize": 1,
|
500
|
+
"maxSize": 3,
|
501
|
+
"desiredSize": 2
|
502
|
+
}
|
503
|
+
|
504
|
+
if node_role_arn is None:
|
505
|
+
node_role_arn = self._create_or_get_nodegroup_role()
|
506
|
+
|
507
|
+
if subnets is None:
|
508
|
+
# Get cluster's subnets
|
509
|
+
cluster_info = self.get_cluster_status(cluster_name)
|
510
|
+
cluster_details = self.eks_client.describe_cluster(name=cluster_name)
|
511
|
+
subnets = cluster_details['cluster']['resourcesVpcConfig']['subnetIds']
|
512
|
+
|
513
|
+
# Create the node group
|
514
|
+
response = self.eks_client.create_nodegroup(
|
515
|
+
clusterName=cluster_name,
|
516
|
+
nodegroupName=nodegroup_name,
|
517
|
+
scalingConfig=scaling_config,
|
518
|
+
instanceTypes=instance_types,
|
519
|
+
amiType=ami_type,
|
520
|
+
capacityType=capacity_type,
|
521
|
+
nodeRole=node_role_arn,
|
522
|
+
subnets=subnets,
|
523
|
+
remoteAccess={
|
524
|
+
'ec2SshKey': None # SSH key is optional
|
525
|
+
},
|
526
|
+
instanceProfile=instance_profile
|
527
|
+
)
|
528
|
+
|
529
|
+
return {
|
530
|
+
'nodegroup_name': nodegroup_name,
|
531
|
+
'cluster_name': cluster_name,
|
532
|
+
'status': 'CREATING',
|
533
|
+
'nodegroup_arn': response['nodegroup']['nodegroupArn'],
|
534
|
+
'instance_types': instance_types,
|
535
|
+
'ami_type': ami_type,
|
536
|
+
'capacity_type': capacity_type,
|
537
|
+
'scaling_config': scaling_config,
|
538
|
+
'created_at': response['nodegroup']['createdAt'],
|
539
|
+
'node_role_arn': node_role_arn,
|
540
|
+
'instance_profile': instance_profile
|
541
|
+
}
|
542
|
+
|
543
|
+
except ClientError as e:
|
544
|
+
raise Exception(f"Failed to create node group with instance profile: {e}")
|
545
|
+
|
546
|
+
def update_nodegroup_scaling_config(self, cluster_name: str, nodegroup_name: str,
|
547
|
+
scaling_config: Dict) -> bool:
|
548
|
+
"""Update the scaling configuration of a node group"""
|
549
|
+
try:
|
550
|
+
# Get current node group configuration
|
551
|
+
nodegroup = self.get_nodegroup_status(cluster_name, nodegroup_name)
|
552
|
+
|
553
|
+
# Update scaling configuration
|
554
|
+
response = self.eks_client.update_nodegroup_config(
|
555
|
+
clusterName=cluster_name,
|
556
|
+
nodegroupName=nodegroup_name,
|
557
|
+
scalingConfig=scaling_config
|
558
|
+
)
|
559
|
+
|
560
|
+
print(f"✅ Node group '{nodegroup_name}' scaling configuration updated")
|
561
|
+
return True
|
562
|
+
|
563
|
+
except ClientError as e:
|
564
|
+
print(f"❌ Error updating node group scaling configuration: {e}")
|
565
|
+
return False
|
566
|
+
|
567
|
+
def update_nodegroup_instance_type(self, cluster_name: str, nodegroup_name: str,
|
568
|
+
instance_types: List[str]) -> bool:
|
569
|
+
"""Update the instance type of a node group"""
|
570
|
+
try:
|
571
|
+
# Get current node group configuration
|
572
|
+
nodegroup = self.get_nodegroup_status(cluster_name, nodegroup_name)
|
573
|
+
|
574
|
+
# Update instance type
|
575
|
+
response = self.eks_client.update_nodegroup_config(
|
576
|
+
clusterName=cluster_name,
|
577
|
+
nodegroupName=nodegroup_name,
|
578
|
+
instanceTypes=instance_types
|
579
|
+
)
|
580
|
+
|
581
|
+
print(f"✅ Node group '{nodegroup_name}' instance type updated")
|
582
|
+
return True
|
583
|
+
|
584
|
+
except ClientError as e:
|
585
|
+
print(f"❌ Error updating node group instance type: {e}")
|
586
|
+
return False
|
587
|
+
|
588
|
+
def delete_nodegroup(self, cluster_name: str, nodegroup_name: str) -> bool:
|
589
|
+
"""Delete an EKS managed node group"""
|
590
|
+
try:
|
591
|
+
self.eks_client.delete_nodegroup(
|
592
|
+
clusterName=cluster_name,
|
593
|
+
nodegroupName=nodegroup_name
|
594
|
+
)
|
595
|
+
print(f"✅ Node group '{nodegroup_name}' deletion initiated")
|
596
|
+
return True
|
597
|
+
except ClientError as e:
|
598
|
+
print(f"❌ Error deleting node group '{nodegroup_name}': {e}")
|
599
|
+
return False
|
600
|
+
|
259
601
|
def get_cluster_status(self, cluster_name: str) -> Dict:
|
260
602
|
"""Get EKS cluster status"""
|
261
603
|
try:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{k8s_helper_cli-0.2.2 → k8s_helper_cli-0.2.4}/src/k8s_helper_cli.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|