Coverage for cli / main.py: 94%
53 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"""
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 capacity,
26 config_cmd,
27 costs,
28 dag,
29 files,
30 inference,
31 jobs,
32 models,
33 nodepools,
34 queue,
35 stacks,
36 templates,
37 webhooks,
38)
39from .config import GCOConfig, get_config
42def _configure_cli_logging(verbose: bool) -> None:
43 """
44 Configure logging for the CLI.
46 By default, the CLI is quiet: only WARNING and above from our own code,
47 and the chatty AWS SDK / HTTP stack loggers (``botocore``, ``boto3``,
48 ``urllib3``, ``s3transfer``, ``kubernetes``) are pinned at WARNING so
49 credential-discovery INFO messages and retry-attempt INFO messages don't
50 clutter normal output.
52 ``--verbose`` / ``-v`` (or ``GCO_LOG_LEVEL=DEBUG``) turns on DEBUG for
53 everything, which is the right escape hatch when something is actually
54 wrong and you need to see what the SDK is doing.
56 This function also calls ``logging.basicConfig`` with ``force=True`` so
57 it overrides any ``basicConfig`` that might have been called at import
58 time by a library module (the CLI owns its log configuration).
59 """
60 env_level = os.environ.get("GCO_LOG_LEVEL")
61 if verbose or (env_level and env_level.upper() == "DEBUG"):
62 level = logging.DEBUG
63 elif env_level: 63 ↛ 64line 63 didn't jump to line 64 because the condition on line 63 was never true
64 level = getattr(logging, env_level.upper(), logging.WARNING)
65 else:
66 level = logging.WARNING
68 logging.basicConfig(
69 level=level,
70 format="%(asctime)s %(levelname)s %(name)s: %(message)s",
71 force=True,
72 )
74 # Pin noisy third-party loggers even when we're at DEBUG, unless the
75 # user explicitly asked for verbose output. This keeps ``-v`` useful
76 # for seeing OUR logs without being drowned by boto's retry chatter.
77 third_party_level = logging.DEBUG if verbose else logging.WARNING
78 for name in ("botocore", "boto3", "urllib3", "s3transfer", "kubernetes"):
79 logging.getLogger(name).setLevel(third_party_level)
82@click.group()
83@click.version_option(version=__version__, prog_name="gco")
84@click.option("--config", "-c", "config_file", help="Path to config file")
85@click.option("--region", "-r", "default_region", help="Default AWS region")
86@click.option(
87 "--output",
88 "-o",
89 "output_format",
90 type=click.Choice(["table", "json", "yaml"]),
91 default="table",
92 help="Output format",
93)
94@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
95@click.option(
96 "--regional-api",
97 is_flag=True,
98 envvar="GCO_REGIONAL_API",
99 help="Use regional API endpoints (for private access when public is disabled)",
100)
101@click.pass_context
102def cli(
103 ctx: click.Context,
104 config_file: str | None,
105 default_region: str | None,
106 output_format: str | None,
107 verbose: bool,
108 regional_api: bool,
109) -> None:
110 """GCO CLI - Manage multi-region EKS clusters for AI/ML workloads."""
111 _configure_cli_logging(verbose)
113 config = get_config()
115 if config_file:
116 config = GCOConfig.from_file(config_file)
117 if default_region:
118 config.default_region = default_region
119 if output_format: 119 ↛ 121line 119 didn't jump to line 121 because the condition on line 119 was always true
120 config.output_format = output_format
121 if verbose:
122 config.verbose = verbose
124 # Store regional_api flag in config for use by aws_client
125 config.use_regional_api = regional_api
127 ctx.obj = config
130# Register command groups
131cli.add_command(jobs)
132cli.add_command(dag)
133cli.add_command(queue)
134cli.add_command(templates)
135cli.add_command(webhooks)
136cli.add_command(capacity)
137cli.add_command(inference)
138cli.add_command(models)
139cli.add_command(nodepools)
140cli.add_command(costs)
141cli.add_command(stacks)
142cli.add_command(files)
143cli.add_command(config_cmd)
146def main() -> None:
147 """Main entry point for the CLI."""
148 cli(obj=None)
151if __name__ == "__main__":
152 main()