atlas-init 0.1.0__py3-none-any.whl → 0.1.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.
Files changed (74) hide show
  1. atlas_init/__init__.py +3 -3
  2. atlas_init/atlas_init.yaml +51 -34
  3. atlas_init/cli.py +76 -72
  4. atlas_init/cli_cfn/app.py +40 -117
  5. atlas_init/cli_cfn/{cfn.py → aws.py} +129 -14
  6. atlas_init/cli_cfn/cfn_parameter_finder.py +89 -6
  7. atlas_init/cli_cfn/example.py +203 -0
  8. atlas_init/cli_cfn/files.py +63 -0
  9. atlas_init/cli_helper/go.py +6 -3
  10. atlas_init/cli_helper/run.py +18 -2
  11. atlas_init/cli_helper/tf_runner.py +12 -21
  12. atlas_init/cli_root/__init__.py +0 -0
  13. atlas_init/cli_root/trigger.py +153 -0
  14. atlas_init/cli_tf/app.py +211 -4
  15. atlas_init/cli_tf/changelog.py +103 -0
  16. atlas_init/cli_tf/debug_logs.py +221 -0
  17. atlas_init/cli_tf/debug_logs_test_data.py +253 -0
  18. atlas_init/cli_tf/github_logs.py +229 -0
  19. atlas_init/cli_tf/go_test_run.py +194 -0
  20. atlas_init/cli_tf/go_test_run_format.py +31 -0
  21. atlas_init/cli_tf/go_test_summary.py +144 -0
  22. atlas_init/cli_tf/hcl/__init__.py +0 -0
  23. atlas_init/cli_tf/hcl/cli.py +161 -0
  24. atlas_init/cli_tf/hcl/cluster_mig.py +348 -0
  25. atlas_init/cli_tf/hcl/parser.py +140 -0
  26. atlas_init/cli_tf/schema.py +222 -18
  27. atlas_init/cli_tf/schema_go_parser.py +236 -0
  28. atlas_init/cli_tf/schema_table.py +150 -0
  29. atlas_init/cli_tf/schema_table_models.py +155 -0
  30. atlas_init/cli_tf/schema_v2.py +599 -0
  31. atlas_init/cli_tf/schema_v2_api_parsing.py +298 -0
  32. atlas_init/cli_tf/schema_v2_sdk.py +361 -0
  33. atlas_init/cli_tf/schema_v3.py +222 -0
  34. atlas_init/cli_tf/schema_v3_sdk.py +279 -0
  35. atlas_init/cli_tf/schema_v3_sdk_base.py +68 -0
  36. atlas_init/cli_tf/schema_v3_sdk_create.py +216 -0
  37. atlas_init/humps.py +253 -0
  38. atlas_init/repos/cfn.py +6 -1
  39. atlas_init/repos/path.py +3 -3
  40. atlas_init/settings/config.py +30 -11
  41. atlas_init/settings/env_vars.py +29 -3
  42. atlas_init/settings/path.py +12 -1
  43. atlas_init/settings/rich_utils.py +39 -2
  44. atlas_init/terraform.yaml +77 -1
  45. atlas_init/tf/.terraform.lock.hcl +125 -0
  46. atlas_init/tf/always.tf +11 -2
  47. atlas_init/tf/main.tf +3 -0
  48. atlas_init/tf/modules/aws_s3/provider.tf +1 -1
  49. atlas_init/tf/modules/aws_vars/aws_vars.tf +2 -0
  50. atlas_init/tf/modules/aws_vpc/provider.tf +4 -1
  51. atlas_init/tf/modules/cfn/cfn.tf +47 -33
  52. atlas_init/tf/modules/cfn/kms.tf +54 -0
  53. atlas_init/tf/modules/cfn/resource_actions.yaml +1 -0
  54. atlas_init/tf/modules/cfn/variables.tf +31 -0
  55. atlas_init/tf/modules/cloud_provider/cloud_provider.tf +1 -0
  56. atlas_init/tf/modules/cloud_provider/provider.tf +1 -1
  57. atlas_init/tf/modules/cluster/cluster.tf +34 -24
  58. atlas_init/tf/modules/cluster/provider.tf +1 -1
  59. atlas_init/tf/modules/federated_vars/federated_vars.tf +3 -0
  60. atlas_init/tf/modules/federated_vars/provider.tf +1 -1
  61. atlas_init/tf/modules/project_extra/project_extra.tf +15 -1
  62. atlas_init/tf/modules/stream_instance/stream_instance.tf +1 -1
  63. atlas_init/tf/modules/vpc_peering/vpc_peering.tf +1 -1
  64. atlas_init/tf/modules/vpc_privatelink/versions.tf +1 -1
  65. atlas_init/tf/outputs.tf +11 -3
  66. atlas_init/tf/providers.tf +2 -1
  67. atlas_init/tf/variables.tf +17 -0
  68. atlas_init/typer_app.py +76 -0
  69. {atlas_init-0.1.0.dist-info → atlas_init-0.1.4.dist-info}/METADATA +58 -21
  70. atlas_init-0.1.4.dist-info/RECORD +91 -0
  71. {atlas_init-0.1.0.dist-info → atlas_init-0.1.4.dist-info}/WHEEL +1 -1
  72. atlas_init-0.1.0.dist-info/RECORD +0 -61
  73. /atlas_init/tf/modules/aws_vpc/{aws-vpc.tf → aws_vpc.tf} +0 -0
  74. {atlas_init-0.1.0.dist-info → atlas_init-0.1.4.dist-info}/entry_points.txt +0 -0
atlas_init/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.4"
4
4
 
5
5
 
6
6
  def running_in_repo() -> bool:
@@ -8,5 +8,5 @@ def running_in_repo() -> bool:
8
8
  if py_directory.name != "py":
9
9
  return False
10
10
  git_directory = py_directory.parent / ".git"
11
- fetch_head = git_directory / "FETCH_HEAD"
12
- return git_directory.exists() and fetch_head.exists() and "atlas-init" in fetch_head.read_text()
11
+ git_config = git_directory / "config"
12
+ return git_directory.exists() and git_config.exists() and "atlas-init" in git_config.read_text()
@@ -1,69 +1,86 @@
1
1
  repo_aliases:
2
- mongodb/terraform-provider-mongodbatlas: tf
3
- mongodb/mongodbatlas-cloudformation-resources: cfn
2
+ mongodb/terraform-provider-mongodbatlas: tf
3
+ mongodb/mongodbatlas-cloudformation-resources: cfn
4
4
  test_suites:
5
5
  - name: cluster
6
6
  repo_go_packages:
7
7
  cfn:
8
- - 'cfn-resources/cluster'
8
+ - cfn-resources/cluster
9
+ - name: clusterm10
10
+ vars:
11
+ cluster_info_m10: true
9
12
  - name: federated
10
13
  repo_go_packages:
11
14
  tf:
12
- - 'internal/service/federatedsettingsorgrolemapping'
15
+ - internal/service/federatedsettingsorgrolemapping
13
16
  vars:
14
17
  use_federated_vars: true
15
- - name: project
16
- vars:
17
- use_project_extra: true
18
- repo_go_packages:
19
- cfn:
20
- - 'cfn-resources/project'
21
- - name: network_peering
22
- vars:
23
- use_aws_vpc: true
24
- use_aws_vars: true
25
- use_vpc_peering: true
18
+ - name: network
26
19
  repo_go_packages:
27
20
  tf:
28
- - 'internal/service/networkpeering'
29
- - name: network
30
- vars:
21
+ - internal/service/privatelinkendpointservicedatafederationonlinearchive
22
+ vars:
31
23
  use_private_link: true
24
+ - name: network_peering
32
25
  repo_go_packages:
33
26
  tf:
34
- - 'internal/service/privatelinkendpointservicedatafederationonlinearchive'
35
- - name: privatelink
27
+ - internal/service/networkpeering
36
28
  vars:
37
- use_aws_vpc: true
29
+ use_vpc_peering: true
38
30
  use_aws_vars: true
31
+ use_aws_vpc: true
32
+ - name: private_endpoint_regional_mode
39
33
  repo_go_packages:
40
34
  tf:
41
- - 'internal/service/privatelinkendpointservice'
42
- - name: stream_connection
43
- repo_go_packages:
44
- tf:
45
- - 'internal/service/streamconnection'
35
+ - internal/service/privateendpointregionalmode
36
+ vars:
37
+ use_aws_vars: true
38
+ - name: privatelink
39
+ repo_go_packages:
46
40
  cfn:
47
- - 'cfn-resources/stream-connection'
41
+ - cfn-resources/cfn-private-endpoint-aws
42
+ tf:
43
+ - internal/service/privatelinkendpointservice
48
44
  vars:
49
- cluster_info: true
50
- stream_instance: true
51
- - name: search_index
45
+ use_aws_vars: true
46
+ use_aws_vpc: true
47
+ - name: project
52
48
  repo_go_packages:
49
+ cfn:
50
+ - cfn-resources/project
53
51
  tf:
54
- - 'internal/service/searchindex'
52
+ - internal/service/project
55
53
  vars:
56
- cluster_info: true
54
+ use_project_extra: true
55
+ - name: resource_policy
56
+ repo_go_packages:
57
+ cfn:
58
+ - cfn-resources/resource-policy
57
59
  - name: s3
58
60
  repo_go_packages:
59
61
  tf:
60
- - 'internal/service/cloudbackupsnapshotexportbucket'
62
+ - internal/service/cloudbackupsnapshotexportbucket
61
63
  vars:
62
64
  use_aws_s3: true
63
65
  - name: s3_with_cluster
64
66
  repo_go_packages:
65
67
  tf:
66
- - 'internal/service/cloudbackupsnapshotexportjob'
68
+ - internal/service/cloudbackupsnapshotexportjob
67
69
  vars:
70
+ cluster_info_m10: true
68
71
  use_aws_s3: true
72
+ - name: search_index
73
+ repo_go_packages:
74
+ tf:
75
+ - internal/service/searchindex
76
+ vars:
77
+ cluster_info: true
78
+ - name: stream_connection
79
+ repo_go_packages:
80
+ tf:
81
+ - internal/service/streamconnection
82
+ cfn:
83
+ - cfn-resources/stream-connection
84
+ vars:
69
85
  cluster_info_m10: true
86
+ stream_instance: true
atlas_init/cli.py CHANGED
@@ -1,18 +1,19 @@
1
1
  import logging
2
- import sys
3
2
  from collections.abc import Callable
4
- from functools import partial
3
+ from pathlib import Path
5
4
  from pydoc import locate
5
+ from typing import Literal
6
6
 
7
7
  import typer
8
+ from model_lib import dump, parse_payload
8
9
  from zero_3rdparty.file_utils import iter_paths
9
10
 
10
- from atlas_init import running_in_repo
11
- from atlas_init.cli_cfn.app import app as app_cfn
12
11
  from atlas_init.cli_helper import sdk_auto_changes
12
+ from atlas_init.cli_helper.go import run_go_tests
13
13
  from atlas_init.cli_helper.run import (
14
14
  run_binary_command_is_ok,
15
15
  run_command_exit_on_failure,
16
+ run_command_receive_result,
16
17
  )
17
18
  from atlas_init.cli_helper.sdk import (
18
19
  SDK_VERSION_HELP,
@@ -31,7 +32,6 @@ from atlas_init.cli_helper.tf_runner import (
31
32
  get_tf_vars,
32
33
  run_terraform,
33
34
  )
34
- from atlas_init.cli_tf.app import app as app_tf
35
35
  from atlas_init.repos.go_sdk import go_sdk_breaking_changes
36
36
  from atlas_init.repos.path import (
37
37
  Repo,
@@ -43,11 +43,7 @@ from atlas_init.repos.path import (
43
43
  )
44
44
  from atlas_init.settings.config import RepoAliasNotFoundError
45
45
  from atlas_init.settings.env_vars import (
46
- DEFAULT_PROFILE,
47
- AtlasInitSettings,
48
46
  active_suites,
49
- as_env_var_name,
50
- env_var_names,
51
47
  init_settings,
52
48
  )
53
49
  from atlas_init.settings.path import (
@@ -55,57 +51,9 @@ from atlas_init.settings.path import (
55
51
  dump_vscode_dotenv,
56
52
  repo_path_rel_path,
57
53
  )
58
- from atlas_init.settings.rich_utils import configure_logging
54
+ from atlas_init.typer_app import app, app_command, extra_root_commands
59
55
 
60
56
  logger = logging.getLogger(__name__)
61
- app = typer.Typer(name="atlas_init", invoke_without_command=True, no_args_is_help=True)
62
- app.add_typer(app_cfn, name="cfn")
63
- app.add_typer(app_tf, name="tf")
64
-
65
- app_command = partial(
66
- app.command,
67
- context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
68
- )
69
-
70
-
71
- @app.callback(invoke_without_command=True)
72
- def main(
73
- ctx: typer.Context,
74
- log_level: str = typer.Option("INFO", help="use one of [INFO, WARNING, ERROR, CRITICAL]"),
75
- profile: str = typer.Option(
76
- DEFAULT_PROFILE,
77
- "-p",
78
- "--profile",
79
- envvar=env_var_names("profile"),
80
- help="used to load .env_manual, store terraform state and variables, and dump .env files.",
81
- ),
82
- project_name: str = typer.Option(
83
- "",
84
- "--project",
85
- envvar=env_var_names("project_name"),
86
- help="atlas project name to create",
87
- ),
88
- ):
89
- explicit_env_vars: dict[str, str] = {}
90
- if project_name != "":
91
- explicit_env_vars[as_env_var_name("project_name")] = project_name
92
- configure_logging(log_level)
93
- logger.info(f"running in repo: {running_in_repo()} python location:{sys.executable}")
94
- missing_env_vars, ambiguous_env_vars = AtlasInitSettings.check_env_vars(
95
- profile,
96
- required_extra_fields=["project_name"],
97
- explicit_env_vars=explicit_env_vars,
98
- )
99
- if missing_env_vars:
100
- typer.echo(f"missing env_vars: {missing_env_vars}")
101
- if ambiguous_env_vars:
102
- typer.echo(
103
- f"amiguous env_vars: {missing_env_vars} (specified both in cli & in .env-manual file with different values)"
104
- )
105
- if missing_env_vars or ambiguous_env_vars:
106
- raise typer.Exit(1)
107
- command = ctx.invoked_subcommand
108
- logger.info(f"in the app callback, log-level: {log_level}, command: {command}")
109
57
 
110
58
 
111
59
  @app_command()
@@ -116,12 +64,19 @@ def init(context: typer.Context):
116
64
  run_terraform(settings, "init", extra_args)
117
65
 
118
66
 
67
+ @app_command()
68
+ def plan(context: typer.Context, *, skip_outputs: bool = False):
69
+ _plan_or_apply(context.args, "plan", skip_outputs=skip_outputs)
70
+
71
+
119
72
  @app_command()
120
73
  def apply(context: typer.Context, *, skip_outputs: bool = False):
74
+ _plan_or_apply(context.args, "apply", skip_outputs=skip_outputs)
75
+
76
+
77
+ def _plan_or_apply(extra_args: list[str], command: Literal["plan", "apply"], *, skip_outputs: bool):
121
78
  settings = init_settings()
122
- extra_args = context.args
123
- logger.info(f"apply extra args: {extra_args}")
124
- logger.info("in the apply command")
79
+ logger.info(f"using the '{command}' command, extra args: {extra_args}")
125
80
  try:
126
81
  suites = active_suites(settings)
127
82
  except (CwdIsNoRepoPathError, RepoAliasNotFoundError) as e:
@@ -132,10 +87,10 @@ def apply(context: typer.Context, *, skip_outputs: bool = False):
132
87
  dump_tf_vars(settings, tf_vars)
133
88
 
134
89
  try:
135
- run_terraform(settings, "apply", extra_args)
90
+ run_terraform(settings, command, extra_args)
136
91
  except TerraformRunError as e:
137
92
  logger.error(repr(e)) # noqa: TRY400
138
- return
93
+ raise typer.Exit(1) from e
139
94
 
140
95
  if not skip_outputs:
141
96
  export_outputs(settings)
@@ -149,6 +104,9 @@ def apply(context: typer.Context, *, skip_outputs: bool = False):
149
104
  def destroy(context: typer.Context):
150
105
  extra_args = context.args
151
106
  settings = init_settings()
107
+ if not settings.tf_state_path.exists():
108
+ logger.warning(f"no terraform state found {settings.tf_state_path}, exiting")
109
+ return
152
110
  tf_vars = get_tf_vars(settings, [])
153
111
  dump_tf_vars(settings, tf_vars)
154
112
  try:
@@ -164,9 +122,15 @@ def test_go():
164
122
  suites = active_suites(settings)
165
123
  sorted_suites = sorted(suite.name for suite in suites)
166
124
  logger.info(f"running go tests for {len(suites)} test-suites: {sorted_suites}")
167
- raise NotImplementedError("fix me later!") # noqa
168
- # package_prefix = settings.config.go_package_prefix(repo_alias)
169
- # run_go_tests(repo_path, repo_alias, package_prefix, settings, active_suites)
125
+ match repo_alias := current_repo():
126
+ case Repo.CFN:
127
+ raise NotImplementedError
128
+ case Repo.TF:
129
+ repo_path = current_repo_path()
130
+ package_prefix = settings.config.go_package_prefix(repo_alias)
131
+ run_go_tests(repo_path, repo_alias, package_prefix, settings, suites)
132
+ case _:
133
+ raise NotImplementedError
170
134
 
171
135
 
172
136
  @app_command()
@@ -186,8 +150,10 @@ def sdk_upgrade(
186
150
 
187
151
  sdk_breaking_changes_path = go_sdk_breaking_changes(repo_path)
188
152
  all_breaking_changes = parse_breaking_changes(sdk_breaking_changes_path, old, new)
189
- replace_in = f"go.mongodb.org/atlas-sdk/{old}/admin"
190
- replace_out = f"go.mongodb.org/atlas-sdk/{new}/admin"
153
+ replacements = {
154
+ f"go.mongodb.org/atlas-sdk/{old}/mockadmin": f"go.mongodb.org/atlas-sdk/{new}/mockadmin",
155
+ f"go.mongodb.org/atlas-sdk/{old}/admin": f"go.mongodb.org/atlas-sdk/{new}/admin",
156
+ }
191
157
  auto_modifier: Callable[[str, str], str] | None = None
192
158
  if auto_change_name:
193
159
  func_path = f"{sdk_auto_changes.__name__}.{auto_change_name}"
@@ -198,7 +164,7 @@ def sdk_upgrade(
198
164
  resources_breaking_changes: set[str] = set()
199
165
  for path in iter_paths(repo_path, "*.go", ".mockery.yaml"):
200
166
  text_old = path.read_text()
201
- if replace_in not in text_old:
167
+ if all(replace_in not in text_old for replace_in in replacements):
202
168
  continue
203
169
  r_name = resource_name(repo_path, path)
204
170
  if resource and resource != r_name:
@@ -210,7 +176,9 @@ def sdk_upgrade(
210
176
  logger.warning(f"found breaking changes: {changes_formatted}")
211
177
  if is_removed(breaking_changes):
212
178
  resources_breaking_changes.add(r_name)
213
- text_new = text_old.replace(replace_in, replace_out)
179
+ text_new = text_old
180
+ for replace_in, replace_out in replacements.items():
181
+ text_new = text_new.replace(replace_in, replace_out)
214
182
  if not dry_run:
215
183
  if auto_modifier:
216
184
  text_new = auto_modifier(text_new, old)
@@ -237,18 +205,22 @@ def sdk_upgrade(
237
205
  def pre_commit(
238
206
  skip_build: bool = typer.Option(default=False),
239
207
  skip_lint: bool = typer.Option(default=False),
208
+ max_issues: int = typer.Option(1000, "-m", "--max"),
240
209
  ):
210
+ golang_ci_lint_args = f"--max-same-issues {max_issues} --max-issues-per-linter {max_issues}"
241
211
  match current_repo():
242
212
  case Repo.CFN:
243
213
  repo_path, resource_path, r_name = find_paths()
244
214
  build_cmd = f"cd {resource_path} && make build"
245
215
  # TODO: understand why piping to grep doesn't work
246
216
  # f"golangci-lint run --path-prefix=./cfn-resources | grep {r_name}"
247
- format_cmd_str = "cd cfn-resources && golangci-lint run --path-prefix=./cfn-resources"
217
+ format_cmd_str = (
218
+ f"cd cfn-resources && golangci-lint run --path-prefix=./cfn-resources {golang_ci_lint_args}"
219
+ )
248
220
  case Repo.TF:
249
221
  repo_path = current_repo_path()
250
222
  build_cmd = "make build"
251
- format_cmd_str = "golangci-lint run"
223
+ format_cmd_str = f"golangci-lint run {golang_ci_lint_args}"
252
224
  case _:
253
225
  raise NotImplementedError
254
226
  if skip_build:
@@ -261,7 +233,39 @@ def pre_commit(
261
233
  run_command_exit_on_failure(format_cmd_str, cwd=repo_path, logger=logger)
262
234
 
263
235
 
236
+ @app_command()
237
+ def repo_dump():
238
+ code_root = Path.home() / "code"
239
+ path_urls = {}
240
+ for repo_git_path in iter_paths(code_root, ".git", exclude_folder_names=[".terraform"]):
241
+ repo_path = repo_git_path.parent
242
+ logger.info(f"repo: {repo_path}")
243
+ url = run_command_receive_result("git remote get-url origin", cwd=repo_path, logger=logger, can_fail=True)
244
+ if url.startswith("FAIL:"):
245
+ continue
246
+ path_urls[str(repo_path)] = url
247
+ out_path = code_root / "repos.json"
248
+ repos_json = dump(path_urls, "pretty_json")
249
+ out_path.write_text(repos_json)
250
+
251
+
252
+ @app_command()
253
+ def repo_clone():
254
+ repos_file = Path.home() / "code" / "repos.json"
255
+ repo_path_json: dict[str, str] = parse_payload(repos_file) # type: ignore
256
+ for repo_path_str, url in repo_path_json.items():
257
+ logger.info(f"cloning {url} @ {repo_path_str}")
258
+ repo_path = Path(repo_path_str)
259
+ parent_dir = repo_path.parent
260
+ parent_dir.mkdir(parents=True, exist_ok=True)
261
+ if repo_path.exists():
262
+ logger.warning(f"skipping {repo_path}, already exists")
263
+ continue
264
+ run_command_exit_on_failure(f"git clone {url} {repo_path.name}", cwd=parent_dir, logger=logger)
265
+
266
+
264
267
  def typer_main():
268
+ extra_root_commands()
265
269
  app()
266
270
 
267
271
 
atlas_init/cli_cfn/app.py CHANGED
@@ -1,42 +1,35 @@
1
1
  import logging
2
2
  import os
3
- from pathlib import Path
4
3
 
5
4
  import typer
6
- from model_lib import dump, parse_payload
7
- from rich import prompt
5
+ from model_lib import parse_payload
8
6
  from zero_3rdparty.file_utils import clean_dir
9
7
 
10
- from atlas_init.cli_args import parse_key_values, parse_key_values_any
11
- from atlas_init.cli_cfn.cfn import (
8
+ from atlas_init.cli_cfn.aws import (
12
9
  activate_resource_type,
13
- create_stack,
14
10
  deactivate_third_party_type,
15
- delete_stack,
16
11
  deregister_cfn_resource_type,
17
12
  get_last_cfn_type,
18
- update_stack,
13
+ wait_on_stack_ok,
14
+ )
15
+ from atlas_init.cli_cfn.aws import (
16
+ delete_stack as delete_stack_aws,
19
17
  )
20
18
  from atlas_init.cli_cfn.cfn_parameter_finder import (
21
- check_execution_role,
22
- decode_parameters,
23
- infer_template_path,
24
19
  read_execution_role,
25
20
  )
21
+ from atlas_init.cli_cfn.example import example_cmd
22
+ from atlas_init.cli_cfn.files import create_sample_file, has_md_link, iterate_schemas
26
23
  from atlas_init.cli_helper.run import run_command_is_ok
27
24
  from atlas_init.cloud.aws import run_in_regions
28
25
  from atlas_init.repos.cfn import (
29
- CfnOperation,
30
- CfnType,
31
- Operation,
32
- infer_cfn_type_name,
33
26
  validate_type_name_regions,
34
27
  )
35
- from atlas_init.repos.path import Repo, current_dir, find_paths
28
+ from atlas_init.repos.path import Repo, current_dir, find_paths, resource_root
36
29
  from atlas_init.settings.env_vars import active_suites, init_settings
37
- from atlas_init.settings.interactive import confirm
38
30
 
39
31
  app = typer.Typer(no_args_is_help=True)
32
+ app.command(name="example")(example_cmd)
40
33
  logger = logging.getLogger(__name__)
41
34
 
42
35
 
@@ -92,104 +85,6 @@ def dereg(
92
85
  run_in_regions(deactivate, regions)
93
86
 
94
87
 
95
- @app.command()
96
- def example(
97
- type_name: str = typer.Argument(default_factory=infer_cfn_type_name),
98
- region: str = typer.Argument(...),
99
- stack_name: str = typer.Argument(...),
100
- operation: str = typer.Argument(...),
101
- params: list[str] = typer.Option(..., "-p", default_factory=list),
102
- resource_params: list[str] = typer.Option(..., "-r", default_factory=list),
103
- stack_timeout_s: int = typer.Option(300, "-t", "--stack-timeout-s"),
104
- ):
105
- params_parsed: dict[str, str] = {}
106
- if params:
107
- params_parsed = parse_key_values(params)
108
- resource_params_parsed = {}
109
- if resource_params:
110
- resource_params_parsed = parse_key_values_any(resource_params)
111
- if resource_params_parsed:
112
- logger.info(f"using resource params: {resource_params_parsed}")
113
- logger.info(f"about to update stack {stack_name} for {type_name} in {region} with {operation}, params: {params}")
114
- settings = init_settings()
115
- type_name, region = CfnType.validate_type_region(type_name, region) # type: ignore
116
- CfnOperation(operaton=operation) # type: ignore
117
- repo_path, resource_path, _ = find_paths(Repo.CFN)
118
- env_vars_generated = settings.load_env_vars_generated()
119
- cfn_execution_role = check_execution_role(repo_path, env_vars_generated)
120
-
121
- cfn_type_details = get_last_cfn_type(type_name, region, is_third_party=False)
122
- logger.info(f"found cfn_type_details {cfn_type_details} for {type_name}")
123
- submit_cmd = f"cfn submit --verbose --set-default --region {region} --role-arn {cfn_execution_role}"
124
- if cfn_type_details is None and confirm(
125
- f"No existing {type_name} found, ok to run:\n{submit_cmd}\nsubmit?",
126
- is_interactive=settings.is_interactive,
127
- default=True,
128
- ):
129
- assert run_command_is_ok(cmd=submit_cmd.split(), env=None, cwd=resource_path, logger=logger)
130
- cfn_type_details = get_last_cfn_type(type_name, region, is_third_party=False)
131
- assert cfn_type_details, f"no cfn_type_details found for {type_name}"
132
-
133
- if operation == Operation.DELETE:
134
- delete_stack(region, stack_name)
135
- return
136
- template_path = infer_template_path(repo_path, type_name, stack_name)
137
- template_path, parameters, not_found = decode_parameters(
138
- exported_env_vars=env_vars_generated,
139
- template_path=template_path,
140
- stack_name=stack_name,
141
- force_params=params_parsed,
142
- resource_params=resource_params_parsed,
143
- type_name=type_name,
144
- )
145
- logger.info(f"parameters: {parameters}")
146
- if not_found:
147
- # TODO: support specifying these extra
148
- logger.critical(f"need to fill out parameters manually: {not_found} for {type_name}")
149
- raise typer.Exit(1)
150
- if not prompt.Confirm("parameters 👆looks good?")():
151
- raise typer.Exit(1)
152
- if operation == Operation.CREATE:
153
- create_stack(
154
- stack_name,
155
- template_str=template_path.read_text(),
156
- region_name=region,
157
- role_arn=cfn_execution_role,
158
- parameters=parameters,
159
- timeout_seconds=stack_timeout_s,
160
- )
161
- elif operation == Operation.UPDATE:
162
- update_stack(
163
- stack_name,
164
- template_str=template_path.read_text(),
165
- region_name=region,
166
- parameters=parameters,
167
- role_arn=cfn_execution_role,
168
- timeout_seconds=stack_timeout_s,
169
- )
170
- else:
171
- raise NotImplementedError
172
-
173
-
174
- def _create_sample_file(
175
- samples_file: Path,
176
- log_group_name: str,
177
- resource_state: dict,
178
- prev_resource_state: dict | None = None,
179
- ):
180
- logger.info(f"adding sample @ {samples_file}")
181
- assert isinstance(resource_state, dict)
182
- new_json = dump(
183
- {
184
- "providerLogGroupName": log_group_name,
185
- "previousResourceState": prev_resource_state or {},
186
- "desiredResourceState": resource_state,
187
- },
188
- "json",
189
- )
190
- samples_file.write_text(new_json)
191
-
192
-
193
88
  @app.command()
194
89
  def inputs(
195
90
  context: typer.Context,
@@ -240,11 +135,11 @@ def inputs(
240
135
  assert isinstance(resource_state, dict), f"input file with not a dict {resource_state}"
241
136
  samples_file = samples_dir / file.name
242
137
  if file.name.endswith("_create.json"):
243
- _create_sample_file(samples_file, log_group_name, resource_state)
138
+ create_sample_file(samples_file, log_group_name, resource_state)
244
139
  if file.name.endswith("_update.json"):
245
140
  prev_state_path = file.parent / file.name.replace("_update.json", "_create.json")
246
141
  prev_state: dict = parse_payload(prev_state_path) # type: ignore
247
- _create_sample_file(
142
+ create_sample_file(
248
143
  samples_file,
249
144
  log_group_name,
250
145
  resource_state,
@@ -256,3 +151,31 @@ def inputs(
256
151
  new_filename = inputs_dir / new_name
257
152
  file.rename(new_filename)
258
153
  logger.info(f"renamed from {file} -> {new_filename}")
154
+
155
+
156
+ @app.command()
157
+ def gen_docs():
158
+ repo_path, *_ = find_paths(Repo.CFN)
159
+ root = resource_root(repo_path)
160
+ for path, schema in iterate_schemas(root):
161
+ if has_md_link(schema.description):
162
+ logger.warning(f"found md link in {schema.type_name} in {path}")
163
+
164
+
165
+ @app.command()
166
+ def wait_on_stack(
167
+ stack_name: str = typer.Argument(...),
168
+ region: str = typer.Argument(...),
169
+ timeout_s: int = typer.Option(300, "-t", "--timeout-seconds"),
170
+ ):
171
+ wait_on_stack_ok(stack_name, region, timeout_seconds=timeout_s)
172
+ logger.info(f"stack {stack_name} in {region} is ready ✅")
173
+
174
+
175
+ @app.command()
176
+ def delete_stack(
177
+ stack_name: str = typer.Argument(...),
178
+ region: str = typer.Argument(...),
179
+ ):
180
+ delete_stack_aws(region, stack_name)
181
+ logger.info(f"stack {stack_name} in {region} is deleted ✅")