Coverage for cli/main.py: 94%

57 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-15 15:07 +0000

1""" 

2GCO CLI - Main entry point. 

3 

4A comprehensive CLI for managing GCO multi-region EKS clusters. 

5 

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 

14 

15Full reference: docs/CLI.md 

16""" 

17 

18import logging 

19import os 

20 

21import click 

22 

23from . import __version__ 

24from .commands import ( 

25 analytics, 

26 capacity, 

27 config_cmd, 

28 costs, 

29 dag, 

30 files, 

31 images, 

32 inference, 

33 jobs, 

34 mission_cmd, 

35 models, 

36 nodepools, 

37 queue, 

38 stacks, 

39 tasks, 

40 templates, 

41 webhooks, 

42) 

43from .config import GCOConfig, get_config 

44 

45 

46def _configure_cli_logging(verbose: bool) -> None: 

47 """ 

48 Configure logging for the CLI. 

49 

50 By default, the CLI is quiet: only WARNING and above from our own code, 

51 and the chatty AWS SDK / HTTP stack loggers (``botocore``, ``boto3``, 

52 ``urllib3``, ``s3transfer``, ``kubernetes``) are pinned at WARNING so 

53 credential-discovery INFO messages and retry-attempt INFO messages don't 

54 clutter normal output. 

55 

56 ``--verbose`` / ``-v`` (or ``GCO_LOG_LEVEL=DEBUG``) turns on DEBUG for 

57 everything, which is the right escape hatch when something is actually 

58 wrong and you need to see what the SDK is doing. 

59 

60 This function also calls ``logging.basicConfig`` with ``force=True`` so 

61 it overrides any ``basicConfig`` that might have been called at import 

62 time by a library module (the CLI owns its log configuration). 

63 """ 

64 env_level = os.environ.get("GCO_LOG_LEVEL") 

65 if verbose or (env_level and env_level.upper() == "DEBUG"): 

66 level = logging.DEBUG 

67 elif env_level: 67 ↛ 68line 67 didn't jump to line 68 because the condition on line 67 was never true

68 level = getattr(logging, env_level.upper(), logging.WARNING) 

69 else: 

70 level = logging.WARNING 

71 

72 logging.basicConfig( 

73 level=level, 

74 format="%(asctime)s %(levelname)s %(name)s: %(message)s", 

75 force=True, 

76 ) 

77 

78 # Pin noisy third-party loggers even when we're at DEBUG, unless the 

79 # user explicitly asked for verbose output. This keeps ``-v`` useful 

80 # for seeing OUR logs without being drowned by boto's retry chatter. 

81 third_party_level = logging.DEBUG if verbose else logging.WARNING 

82 for name in ("botocore", "boto3", "urllib3", "s3transfer", "kubernetes"): 

83 logging.getLogger(name).setLevel(third_party_level) 

84 

85 

86@click.group() 

87@click.version_option(version=__version__, prog_name="gco") 

88@click.option("--config", "-c", "config_file", help="Path to config file") 

89@click.option("--region", "-r", "default_region", help="Default AWS region") 

90@click.option( 

91 "--output", 

92 "-o", 

93 "output_format", 

94 type=click.Choice(["table", "json", "yaml"]), 

95 default="table", 

96 help="Output format", 

97) 

98@click.option("--verbose", "-v", is_flag=True, help="Verbose output") 

99@click.option( 

100 "--regional-api", 

101 is_flag=True, 

102 envvar="GCO_REGIONAL_API", 

103 help="Use regional API endpoints (for private access when public is disabled)", 

104) 

105@click.pass_context 

106def cli( 

107 ctx: click.Context, 

108 config_file: str | None, 

109 default_region: str | None, 

110 output_format: str | None, 

111 verbose: bool, 

112 regional_api: bool, 

113) -> None: 

114 """GCO CLI - Manage multi-region EKS clusters for AI/ML workloads.""" 

115 _configure_cli_logging(verbose) 

116 

117 config = get_config() 

118 

119 if config_file: 

120 config = GCOConfig.from_file(config_file) 

121 if default_region: 

122 config.default_region = default_region 

123 if output_format: 123 ↛ 125line 123 didn't jump to line 125 because the condition on line 123 was always true

124 config.output_format = output_format 

125 if verbose: 

126 config.verbose = verbose 

127 

128 # Store regional_api flag in config for use by aws_client 

129 config.use_regional_api = regional_api 

130 

131 ctx.obj = config 

132 

133 

134# Register command groups 

135cli.add_command(jobs) 

136cli.add_command(dag) 

137cli.add_command(queue) 

138cli.add_command(templates) 

139cli.add_command(webhooks) 

140cli.add_command(capacity) 

141cli.add_command(inference) 

142cli.add_command(images) 

143cli.add_command(models) 

144cli.add_command(nodepools) 

145cli.add_command(costs) 

146cli.add_command(stacks) 

147cli.add_command(files) 

148cli.add_command(config_cmd) 

149cli.add_command(analytics) 

150cli.add_command(tasks) 

151cli.add_command(mission_cmd) 

152 

153 

154def main() -> None: 

155 """Main entry point for the CLI.""" 

156 cli(obj=None) 

157 

158 

159if __name__ == "__main__": 

160 main()