k8s-helper-cli 0.2.3__py3-none-any.whl → 0.2.4__py3-none-any.whl

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/__init__.py CHANGED
@@ -20,7 +20,7 @@ from .utils import (
20
20
  create_service_manifest
21
21
  )
22
22
 
23
- __version__ = "0.2.3"
23
+ __version__ = "0.2.4"
24
24
  __author__ = "Harshit Chatterjee"
25
25
  __email__ = "harshitchatterjee50@gmail.com"
26
26
 
k8s_helper/cli.py CHANGED
@@ -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 Requirements:")
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 node_group:
701
- console.print(" • Managed node group with EC2 instances")
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
- console.print(f" 2. Verify connection: kubectl get svc")
733
- console.print(f" 3. Deploy applications: k8s-helper apply <app-name> <image>")
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
  # ======================
k8s_helper/core.py CHANGED
@@ -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) -> Dict:
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
@@ -98,11 +101,34 @@ class EKSClient:
98
101
  'created_at': cluster_response['cluster']['createdAt']
99
102
  }
100
103
 
101
- # If node group name is provided, we'll create it after cluster is active
102
- if node_group_name:
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
+
103
109
  cluster_info['node_group_name'] = node_group_name
104
110
  cluster_info['instance_types'] = instance_types
105
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"
106
132
 
107
133
  return cluster_info
108
134
 
@@ -260,6 +286,318 @@ class EKSClient:
260
286
  else:
261
287
  raise Exception(f"Failed to create or get cluster role: {e}")
262
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
+
263
601
  def get_cluster_status(self, cluster_name: str) -> Dict:
264
602
  """Get EKS cluster status"""
265
603
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: k8s-helper-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: A simplified Python wrapper for common Kubernetes operations
5
5
  Author-email: Harshit Chatterjee <harshitchatterjee50@gmail.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,11 @@
1
+ k8s_helper/__init__.py,sha256=z_EPSIEXHjmKYPDJGtUJPOK83_xzc-Vu9Ya4_4Tdzlk,2666
2
+ k8s_helper/cli.py,sha256=VCB-GfXWRtgZ74tNVx_uMbJk4Bt9LNAkcWa4xK8UEH0,51280
3
+ k8s_helper/config.py,sha256=P7YdfyvCHprrNs2J9DRb3RrClylfTTh5hfTtDzLug0A,6867
4
+ k8s_helper/core.py,sha256=Pdv4E_6iEkAZ30qYs1LiLTFTrsu2TvrqnIPACn7VN20,63970
5
+ k8s_helper/utils.py,sha256=wYgTd5ktyuI-EiVcfW7FrxA7MzXY5odrEKQgmMVdueY,9496
6
+ k8s_helper_cli-0.2.4.dist-info/licenses/LICENSE,sha256=tXPvVl3gLVc6e0qCEoLH9KjeA7z4JVL78UybpvGtBCw,1096
7
+ k8s_helper_cli-0.2.4.dist-info/METADATA,sha256=2qVzAbjVTPib64gKpn3M_qnTIT8aL4rxJHyJH2Klqxg,26956
8
+ k8s_helper_cli-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ k8s_helper_cli-0.2.4.dist-info/entry_points.txt,sha256=IoCMWUZ6mn90LwzQzEy5YkWOwvogDdZ6ycqUWAzCFTQ,50
10
+ k8s_helper_cli-0.2.4.dist-info/top_level.txt,sha256=x9A1jflyer-z2cFnkqk5B42juoH2q0fy5hkT9upsTG8,11
11
+ k8s_helper_cli-0.2.4.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- k8s_helper/__init__.py,sha256=adDwSsi6HKoSz0Z6QTw9GAwrcn7c1lCn-cik61roBaw,2666
2
- k8s_helper/cli.py,sha256=yphZTElwRv873GcYwAQDoonBK_qbj9VSS3Q8fo1aFIE,38671
3
- k8s_helper/config.py,sha256=P7YdfyvCHprrNs2J9DRb3RrClylfTTh5hfTtDzLug0A,6867
4
- k8s_helper/core.py,sha256=TMiLb0rV_Tmnmq7yiEDjwahUZqCl7pB2qm0VkfmT1rU,49126
5
- k8s_helper/utils.py,sha256=wYgTd5ktyuI-EiVcfW7FrxA7MzXY5odrEKQgmMVdueY,9496
6
- k8s_helper_cli-0.2.3.dist-info/licenses/LICENSE,sha256=tXPvVl3gLVc6e0qCEoLH9KjeA7z4JVL78UybpvGtBCw,1096
7
- k8s_helper_cli-0.2.3.dist-info/METADATA,sha256=gkBlWtBtj02-AGveB2y1X5tXSb0NbYtPLP7AhMHWC94,26956
8
- k8s_helper_cli-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- k8s_helper_cli-0.2.3.dist-info/entry_points.txt,sha256=IoCMWUZ6mn90LwzQzEy5YkWOwvogDdZ6ycqUWAzCFTQ,50
10
- k8s_helper_cli-0.2.3.dist-info/top_level.txt,sha256=x9A1jflyer-z2cFnkqk5B42juoH2q0fy5hkT9upsTG8,11
11
- k8s_helper_cli-0.2.3.dist-info/RECORD,,