Coverage for cli / main.py: 100.00%
67 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
1"""
2GCO CLI - Main entry point.
4A comprehensive CLI for managing GCO multi-region EKS clusters.
6Commands:
7 gco stacks deploy-all -y # Deploy all infrastructure
8 gco jobs submit-sqs job.yaml -r us-east-1 # Submit job via SQS (recommended)
9 gco jobs submit job.yaml -n gco-jobs # Submit job via API Gateway
10 gco jobs list --all-regions # List jobs across regions
11 gco capacity check -t g4dn.xlarge # Check GPU capacity
12 gco inference deploy my-llm -i ... # Deploy inference endpoint
13 gco stacks destroy-all -y # Tear down everything
15Full reference: docs/CLI.md
16"""
18import logging
19import os
21import click
23from . import __version__
24from .commands import (
25 analytics,
26 autopilot,
27 capacity,
28 cluster,
29 config_cmd,
30 costs,
31 dag,
32 deps,
33 examples,
34 files,
35 images,
36 inference,
37 jobs,
38 mission_cmd,
39 models,
40 monitoring,
41 nodepools,
42 queue,
43 release,
44 stacks,
45 status,
46 storage,
47 swarm_cmd,
48 tasks,
49 templates,
50 vector,
51 webhooks,
52)
53from .config import get_config
54from .output import StructuredOutputGroup
57def _configure_cli_logging(verbose: bool) -> None:
58 """
59 Configure logging for the CLI.
61 By default, the CLI is quiet: only WARNING and above from our own code,
62 and the chatty AWS SDK / HTTP stack loggers (``botocore``, ``boto3``,
63 ``urllib3``, ``s3transfer``, ``kubernetes``) are pinned at WARNING so
64 credential-discovery INFO messages and retry-attempt INFO messages don't
65 clutter normal output.
67 ``--verbose`` / ``-v`` (or ``GCO_LOG_LEVEL=DEBUG``) turns on DEBUG for
68 everything, which is the right escape hatch when something is actually
69 wrong and you need to see what the SDK is doing.
71 This function also calls ``logging.basicConfig`` with ``force=True`` so
72 it overrides any ``basicConfig`` that might have been called at import
73 time by a library module (the CLI owns its log configuration).
74 """
75 env_level = os.environ.get("GCO_LOG_LEVEL")
76 if verbose or (env_level and env_level.upper() == "DEBUG"):
77 level = logging.DEBUG
78 elif env_level:
79 level = getattr(logging, env_level.upper(), logging.WARNING)
80 else:
81 level = logging.WARNING
83 logging.basicConfig(
84 level=level,
85 format="%(asctime)s %(levelname)s %(name)s: %(message)s",
86 force=True,
87 )
89 # Pin noisy third-party loggers even when we're at DEBUG, unless the
90 # user explicitly asked for verbose output. This keeps ``-v`` useful
91 # for seeing OUR logs without being drowned by boto's retry chatter.
92 third_party_level = logging.DEBUG if verbose else logging.WARNING
93 for name in ("botocore", "boto3", "urllib3", "s3transfer", "kubernetes"):
94 logging.getLogger(name).setLevel(third_party_level)
97@click.group(cls=StructuredOutputGroup)
98@click.version_option(version=__version__, prog_name="gco")
99@click.option("--config", "-c", "config_file", help="Path to config file")
100@click.option("--region", "-r", "default_region", help="Default AWS region")
101@click.option(
102 "--output",
103 "-o",
104 "output_format",
105 type=click.Choice(["table", "json", "yaml"]),
106 default=None,
107 help="Output format (defaults to the configured value)",
108)
109@click.option("--verbose", "-v", is_flag=True, default=None, help="Verbose output")
110@click.option(
111 "--regional-api/--global-api",
112 default=None,
113 help="Use regional API endpoints, or explicitly use the global endpoint",
114)
115@click.pass_context
116def cli(
117 ctx: click.Context,
118 config_file: str | None,
119 default_region: str | None,
120 output_format: str | None,
121 verbose: bool | None,
122 regional_api: bool | None,
123) -> None:
124 """GCO CLI - Manage multi-region EKS clusters for AI/ML workloads."""
125 config = get_config(config_file)
127 if default_region:
128 config.default_region = default_region
129 if output_format:
130 config.output_format = output_format
131 if verbose is not None:
132 config.verbose = verbose
133 if regional_api is not None:
134 config.use_regional_api = regional_api
136 _configure_cli_logging(config.verbose)
137 ctx.obj = config
140# Register command groups
141cli.add_command(autopilot)
142cli.add_command(jobs)
143cli.add_command(dag)
144cli.add_command(deps)
145cli.add_command(queue)
146cli.add_command(release)
147cli.add_command(examples)
148cli.add_command(templates)
149cli.add_command(webhooks)
150cli.add_command(capacity)
151cli.add_command(cluster)
152cli.add_command(inference)
153cli.add_command(images)
154cli.add_command(models)
155cli.add_command(nodepools)
156cli.add_command(costs)
157cli.add_command(stacks)
158cli.add_command(storage)
159cli.add_command(files)
160cli.add_command(config_cmd)
161cli.add_command(analytics)
162cli.add_command(monitoring)
163cli.add_command(tasks)
164cli.add_command(mission_cmd)
165cli.add_command(swarm_cmd)
166cli.add_command(vector)
167cli.add_command(status)
170def main() -> None:
171 """Main entry point for the CLI."""
172 cli(obj=None)
175if __name__ == "__main__":
176 main()