Coverage for cli / commands / nodepools_cmd.py: 100%
74 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 21:47 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 21:47 +0000
1"""NodePool commands."""
3import sys
4from typing import Any
6import click
8from ..config import GCOConfig
9from ..output import get_output_formatter
11pass_config = click.make_pass_decorator(GCOConfig, ensure=True)
14@click.group()
15@pass_config
16def nodepools(config: Any) -> None:
17 """Manage Karpenter NodePools with ODCR/Capacity Reservation support."""
18 pass
21@nodepools.command("create-odcr")
22@click.option("--name", "-n", required=True, help="NodePool name")
23@click.option("--region", "-r", required=True, help="AWS region")
24@click.option(
25 "--capacity-reservation-id",
26 "-c",
27 required=True,
28 help="EC2 Capacity Reservation ID (cr-xxx) or ODCR group ARN",
29)
30@click.option(
31 "--instance-type",
32 "-i",
33 multiple=True,
34 help="Instance types (can specify multiple)",
35)
36@click.option("--max-nodes", default=100, help="Maximum nodes in pool")
37@click.option("--fallback-on-demand", is_flag=True, help="Fall back to on-demand if ODCR exhausted")
38@click.option(
39 "--efa",
40 is_flag=True,
41 help="Enable EFA support (adds EFA taint and labels for p4d/p5 instances)",
42)
43@click.option("--output-file", "-o", help="Output manifest to file instead of applying")
44@pass_config
45def create_odcr_nodepool(
46 config: Any,
47 name: Any,
48 region: Any,
49 capacity_reservation_id: Any,
50 instance_type: Any,
51 max_nodes: Any,
52 fallback_on_demand: Any,
53 efa: Any,
54 output_file: Any,
55) -> None:
56 """Create a NodePool backed by an On-Demand Capacity Reservation (ODCR).
58 This creates a Karpenter NodePool and EC2NodeClass configured to use
59 a specific capacity reservation for guaranteed capacity.
61 Examples:
62 gco nodepools create-odcr -n gpu-reserved -r us-east-1 \\
63 -c cr-0123456789abcdef0 -i p4d.24xlarge
65 gco nodepools create-odcr -n ml-training -r us-west-2 \\
66 -c cr-0123456789abcdef0 -i p5.48xlarge --fallback-on-demand
68 gco nodepools create-odcr -n efa-training -r us-east-1 \\
69 -c cr-0123456789abcdef0 -i p4d.24xlarge --efa
71 See: https://karpenter.sh/docs/tasks/odcrs/
72 """
73 from ..nodepools import generate_odcr_nodepool_manifest
75 formatter = get_output_formatter(config)
77 try:
78 manifest = generate_odcr_nodepool_manifest(
79 name=name,
80 region=region,
81 capacity_reservation_id=capacity_reservation_id,
82 instance_types=list(instance_type) if instance_type else None,
83 max_nodes=max_nodes,
84 fallback_on_demand=fallback_on_demand,
85 efa=efa,
86 )
88 if output_file:
89 with open(output_file, "w", encoding="utf-8") as f:
90 f.write(manifest)
91 formatter.print_success(f"NodePool manifest written to {output_file}")
92 formatter.print_info(f"Apply with: kubectl apply -f {output_file}")
93 else:
94 # Print the manifest for review
95 print(manifest)
96 formatter.print_info("\nTo apply this manifest, save it to a file and run:")
97 formatter.print_info(" kubectl apply -f <filename>.yaml")
99 except Exception as e:
100 formatter.print_error(f"Failed to create ODCR NodePool: {e}")
101 sys.exit(1)
104@nodepools.command("list")
105@click.option("--region", "-r", help="Filter by region")
106@click.option("--cluster", help="EKS cluster name (defaults to gco-<region>)")
107@pass_config
108def list_nodepools(config: Any, region: Any, cluster: Any) -> None:
109 """List NodePools in the cluster.
111 Examples:
112 gco nodepools list --region us-east-1
113 gco nodepools list --cluster my-cluster
114 """
115 from ..nodepools import list_cluster_nodepools
117 formatter = get_output_formatter(config)
119 try:
120 if not region and not cluster:
121 formatter.print_error("Either --region or --cluster is required")
122 sys.exit(1)
124 cluster_name = cluster or f"gco-{region}"
125 nodepools_list = list_cluster_nodepools(cluster_name, region or config.default_region)
127 if not nodepools_list:
128 formatter.print_info("No NodePools found")
129 return
131 formatter.print(nodepools_list)
133 except Exception as e:
134 formatter.print_error(f"Failed to list NodePools: {e}")
135 sys.exit(1)
138@nodepools.command("describe")
139@click.argument("nodepool_name")
140@click.option("--region", "-r", required=True, help="AWS region")
141@click.option("--cluster", help="EKS cluster name (defaults to gco-<region>)")
142@pass_config
143def describe_nodepool(config: Any, nodepool_name: Any, region: Any, cluster: Any) -> None:
144 """Describe a NodePool.
146 Examples:
147 gco nodepools describe gpu-x86-pool --region us-east-1
148 """
149 from ..nodepools import describe_cluster_nodepool
151 formatter = get_output_formatter(config)
153 try:
154 cluster_name = cluster or f"gco-{region}"
155 nodepool = describe_cluster_nodepool(cluster_name, region, nodepool_name)
157 if not nodepool:
158 formatter.print_error(f"NodePool {nodepool_name} not found")
159 sys.exit(1)
161 formatter.print(nodepool)
163 except Exception as e:
164 formatter.print_error(f"Failed to describe NodePool: {e}")
165 sys.exit(1)