atlas-init 0.7.0__tar.gz → 0.8.1__tar.gz

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 (156) hide show
  1. {atlas_init-0.7.0 → atlas_init-0.8.1}/.gitignore +3 -1
  2. {atlas_init-0.7.0 → atlas_init-0.8.1}/PKG-INFO +4 -3
  3. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/__init__.py +1 -1
  4. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/atlas_init.yaml +1 -0
  5. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/example_update.py +20 -8
  6. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/modifier.py +22 -8
  7. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/env_vars.py +12 -2
  8. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/api_call.py +9 -9
  9. atlas_init-0.8.1/atlas_init/tf_ext/args.py +32 -0
  10. atlas_init-0.8.1/atlas_init/tf_ext/gen_examples.py +141 -0
  11. atlas_init-0.8.1/atlas_init/tf_ext/gen_module_readme.py +131 -0
  12. atlas_init-0.8.1/atlas_init/tf_ext/gen_resource_main.py +195 -0
  13. atlas_init-0.8.1/atlas_init/tf_ext/gen_resource_output.py +71 -0
  14. atlas_init-0.8.1/atlas_init/tf_ext/gen_resource_variables.py +162 -0
  15. atlas_init-0.8.1/atlas_init/tf_ext/gen_versions.py +10 -0
  16. atlas_init-0.8.1/atlas_init/tf_ext/models_module.py +455 -0
  17. atlas_init-0.8.1/atlas_init/tf_ext/newres.py +90 -0
  18. atlas_init-0.8.1/atlas_init/tf_ext/plan_diffs.py +140 -0
  19. atlas_init-0.8.1/atlas_init/tf_ext/provider_schema.py +199 -0
  20. atlas_init-0.8.1/atlas_init/tf_ext/py_gen.py +294 -0
  21. atlas_init-0.8.1/atlas_init/tf_ext/schema_to_dataclass.py +522 -0
  22. atlas_init-0.8.1/atlas_init/tf_ext/settings.py +188 -0
  23. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/tf_dep.py +5 -5
  24. atlas_init-0.8.1/atlas_init/tf_ext/tf_desc_gen.py +53 -0
  25. atlas_init-0.8.1/atlas_init/tf_ext/tf_desc_update.py +0 -0
  26. atlas_init-0.8.1/atlas_init/tf_ext/tf_mod_gen.py +263 -0
  27. atlas_init-0.8.1/atlas_init/tf_ext/tf_mod_gen_provider.py +124 -0
  28. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/tf_modules.py +5 -4
  29. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/tf_vars.py +13 -28
  30. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/typer_app.py +6 -2
  31. {atlas_init-0.7.0 → atlas_init-0.8.1}/pyproject.toml +6 -3
  32. atlas_init-0.7.0/atlas_init/tf_ext/args.py +0 -17
  33. atlas_init-0.7.0/atlas_init/tf_ext/settings.py +0 -39
  34. {atlas_init-0.7.0 → atlas_init-0.8.1}/LICENSE +0 -0
  35. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/__main__.py +0 -0
  36. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli.py +0 -0
  37. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_args.py +0 -0
  38. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/__init__.py +0 -0
  39. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/app.py +0 -0
  40. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/aws.py +0 -0
  41. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/cfn_parameter_finder.py +0 -0
  42. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/contract.py +0 -0
  43. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/example.py +0 -0
  44. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_cfn/files.py +0 -0
  45. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/__init__.py +0 -0
  46. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/go.py +0 -0
  47. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/run.py +0 -0
  48. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/run_manager.py +0 -0
  49. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/sdk.py +0 -0
  50. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/sdk_auto_changes.py +0 -0
  51. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_helper/tf_runner.py +0 -0
  52. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_root/__init__.py +0 -0
  53. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_root/go_test.py +0 -0
  54. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_root/mms_released.py +0 -0
  55. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_root/trigger.py +0 -0
  56. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/__init__.py +0 -0
  57. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/app.py +0 -0
  58. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/changelog.py +0 -0
  59. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/ci_tests.py +0 -0
  60. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/codegen/__init__.py +0 -0
  61. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/codegen/models.py +0 -0
  62. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/codegen/openapi_minimal.py +0 -0
  63. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/debug_logs.py +0 -0
  64. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/debug_logs_test_data.py +0 -0
  65. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/debug_logs_test_data_package_config.py +0 -0
  66. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/github_logs.py +0 -0
  67. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/go_test_run.py +0 -0
  68. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/go_test_summary.py +0 -0
  69. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/go_test_tf_error.py +0 -0
  70. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/__init__.py +0 -0
  71. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/cli.py +0 -0
  72. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/cluster_mig.py +0 -0
  73. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/modifier2.py +0 -0
  74. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/hcl/parser.py +0 -0
  75. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/log_clean.py +0 -0
  76. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/mock_tf_log.py +0 -0
  77. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/openapi.py +0 -0
  78. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema.py +0 -0
  79. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_go_parser.py +0 -0
  80. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_inspection.py +0 -0
  81. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_table.py +0 -0
  82. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_table_models.py +0 -0
  83. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v2.py +0 -0
  84. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v2_sdk.py +0 -0
  85. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v3.py +0 -0
  86. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v3_sdk.py +0 -0
  87. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v3_sdk_base.py +0 -0
  88. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cli_tf/schema_v3_sdk_create.py +0 -0
  89. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cloud/__init__.py +0 -0
  90. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/cloud/aws.py +0 -0
  91. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/crud/__init__.py +0 -0
  92. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/crud/mongo_client.py +0 -0
  93. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/crud/mongo_dao.py +0 -0
  94. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/crud/mongo_utils.py +0 -0
  95. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/html_out/__init__.py +0 -0
  96. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/html_out/md_export.py +0 -0
  97. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/humps.py +0 -0
  98. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/repos/__init__.py +0 -0
  99. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/repos/cfn.py +0 -0
  100. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/repos/go_sdk.py +0 -0
  101. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/repos/path.py +0 -0
  102. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/sdk_ext/__init__.py +0 -0
  103. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/sdk_ext/go.py +0 -0
  104. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/sdk_ext/typer_app.py +0 -0
  105. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/__init__.py +0 -0
  106. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/config.py +0 -0
  107. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/env_vars_generated.py +0 -0
  108. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/env_vars_modules.py +0 -0
  109. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/interactive.py +0 -0
  110. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/interactive2.py +0 -0
  111. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/path.py +0 -0
  112. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/settings/rich_utils.py +0 -0
  113. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/terraform.yaml +0 -0
  114. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/.terraform.lock.hcl +0 -0
  115. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/always.tf +0 -0
  116. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/main.tf +0 -0
  117. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_kms/aws_kms.tf +0 -0
  118. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_kms/provider.tf +0 -0
  119. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_s3/aws_s3.tf +0 -0
  120. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_s3/provider.tf +0 -0
  121. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_vars/aws_vars.tf +0 -0
  122. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_vpc/aws_vpc.tf +0 -0
  123. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/aws_vpc/provider.tf +0 -0
  124. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/assume_role_services.yaml +0 -0
  125. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/cfn.tf +0 -0
  126. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/kms.tf +0 -0
  127. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/provider.tf +0 -0
  128. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/resource_actions.yaml +0 -0
  129. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cfn/variables.tf +0 -0
  130. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cloud_provider/cloud_provider.tf +0 -0
  131. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cloud_provider/provider.tf +0 -0
  132. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cluster/cluster.tf +0 -0
  133. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/cluster/provider.tf +0 -0
  134. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/encryption_at_rest/main.tf +0 -0
  135. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/encryption_at_rest/provider.tf +0 -0
  136. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/federated_vars/federated_vars.tf +0 -0
  137. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/federated_vars/provider.tf +0 -0
  138. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/project_extra/project_extra.tf +0 -0
  139. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/project_extra/provider.tf +0 -0
  140. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/stream_instance/provider.tf +0 -0
  141. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/stream_instance/stream_instance.tf +0 -0
  142. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/vpc_peering/provider.tf +0 -0
  143. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/vpc_peering/vpc_peering.tf +0 -0
  144. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/vpc_privatelink/atlas-privatelink.tf +0 -0
  145. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/vpc_privatelink/variables.tf +0 -0
  146. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/modules/vpc_privatelink/versions.tf +0 -0
  147. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/outputs.tf +0 -0
  148. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/providers.tf +0 -0
  149. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf/variables.tf +0 -0
  150. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/__init__.py +0 -0
  151. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/__main__.py +0 -0
  152. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/constants.py +0 -0
  153. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/models.py +0 -0
  154. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/tf_ext/paths.py +0 -0
  155. {atlas_init-0.7.0 → atlas_init-0.8.1}/atlas_init/typer_app.py +0 -0
  156. {atlas_init-0.7.0 → atlas_init-0.8.1}/readme.md +0 -0
@@ -26,4 +26,6 @@ demo/
26
26
  tf_log/
27
27
  downloads
28
28
  htmlcov
29
- tf_dep/
29
+ tf_dep/
30
+ static
31
+ cache
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlas-init
3
- Version: 0.7.0
3
+ Version: 0.8.1
4
4
  Project-URL: Documentation, https://github.com/EspenAlbert/atlas-init#readme
5
5
  Project-URL: Issues, https://github.com/EspenAlbert/atlas-init/issues
6
6
  Project-URL: Source, https://github.com/EspenAlbert/atlas-init
@@ -12,10 +12,11 @@ Classifier: Programming Language :: Python
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Requires-Python: >=3.13
14
14
  Requires-Dist: appdirs==1.4.4
15
- Requires-Dist: ask-shell>=0.0.4
15
+ Requires-Dist: ask-shell>=0.0.5
16
16
  Requires-Dist: boto3==1.35.92
17
17
  Requires-Dist: gitpython==3.1.42
18
18
  Requires-Dist: humanize==4.9.0
19
+ Requires-Dist: inflection==0.5.1
19
20
  Requires-Dist: model-lib
20
21
  Requires-Dist: motor==3.7.1
21
22
  Requires-Dist: mypy-boto3-cloudformation==1.37.22
@@ -30,7 +31,7 @@ Requires-Dist: rich==14.0.0
30
31
  Requires-Dist: stringcase==1.2.0
31
32
  Requires-Dist: tenacity==9.0.0
32
33
  Requires-Dist: typer>=0.15.1
33
- Requires-Dist: zero-3rdparty
34
+ Requires-Dist: zero-3rdparty==1.0.0b5
34
35
  Description-Content-Type: text/markdown
35
36
 
36
37
  # Atlas Init - A CLI for developing integrations with MongoDB Atlas
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- VERSION = "0.7.0"
3
+ VERSION = "0.8.1"
4
4
 
5
5
 
6
6
  def running_in_repo() -> bool:
@@ -94,6 +94,7 @@ test_suites:
94
94
  vars:
95
95
  cluster_info_m10: true
96
96
  stream_instance: true
97
+ use_aws_vpc: true
97
98
  - name: trigger
98
99
  repo_go_packages:
99
100
  cfn:
@@ -2,26 +2,30 @@ import logging
2
2
  from collections import defaultdict
3
3
  from functools import total_ordering
4
4
  from pathlib import Path
5
+ from typing import Callable
5
6
 
6
7
  import typer
7
8
  from model_lib import Entity, Event, dump, parse_payload
8
- from pydantic import Field
9
+ from pydantic import BaseModel, ConfigDict, Field
9
10
 
10
11
  from atlas_init.cli_helper.run import run_binary_command_is_ok
11
12
  from atlas_init.cli_tf.hcl.modifier import (
12
13
  BLOCK_TYPE_OUTPUT,
13
14
  BLOCK_TYPE_VARIABLE,
15
+ NewDescription,
14
16
  update_descriptions,
15
17
  )
16
18
 
17
19
  logger = logging.getLogger(__name__)
18
20
 
19
21
 
20
- class UpdateExamples(Entity):
22
+ class UpdateExamples(BaseModel):
23
+ model_config = ConfigDict(arbitrary_types_allowed=True)
21
24
  examples_base_dir: Path
22
- var_descriptions: dict[str, str]
25
+ var_descriptions: dict[str, str] = Field(default_factory=dict)
23
26
  output_descriptions: dict[str, str] = Field(default_factory=dict)
24
27
  skip_tf_fmt: bool = False
28
+ new_description_call: Callable[[str, str, Path], str] | None = None # Protocol not supported for Pydantic
25
29
 
26
30
 
27
31
  @total_ordering
@@ -50,16 +54,24 @@ class UpdateExamplesOutput(Entity):
50
54
 
51
55
  def update_examples(event_in: UpdateExamples) -> UpdateExamplesOutput:
52
56
  changes = []
57
+
58
+ def get_description(name: str, old_description: str, path: Path) -> str:
59
+ return event_in.var_descriptions.get(name, "")
60
+
53
61
  existing_var_descriptions = update_block_descriptions(
54
62
  event_in.examples_base_dir,
55
63
  changes,
56
- event_in.var_descriptions,
64
+ event_in.new_description_call or get_description, # type: ignore
57
65
  BLOCK_TYPE_VARIABLE,
58
66
  )
67
+
68
+ def get_output_description(name: str, old_description: str, path: Path) -> str:
69
+ return event_in.output_descriptions.get(name, "")
70
+
59
71
  existing_output_descriptions = update_block_descriptions(
60
72
  event_in.examples_base_dir,
61
73
  changes,
62
- event_in.output_descriptions,
74
+ event_in.new_description_call or get_output_description, # type: ignore
63
75
  BLOCK_TYPE_OUTPUT,
64
76
  )
65
77
  if event_in.skip_tf_fmt:
@@ -85,14 +97,14 @@ def flatten_descriptions(descriptions: dict[str, list[str]]) -> dict[str, str]:
85
97
  def update_block_descriptions(
86
98
  base_dir: Path,
87
99
  changes: list[TFConfigDescriptionChange],
88
- new_names: dict[str, str],
100
+ get_description: NewDescription,
89
101
  block_type: str,
90
102
  ):
91
103
  all_existing_descriptions = defaultdict(list)
92
104
  in_files = sorted(base_dir.rglob("*.tf"))
93
105
  for tf_file in in_files:
94
106
  logger.info(f"looking for {block_type} in {tf_file}")
95
- new_tf, existing_descriptions = update_descriptions(tf_file, new_names, block_type=block_type)
107
+ new_tf, existing_descriptions = update_descriptions(tf_file, get_description, block_type=block_type)
96
108
  if not existing_descriptions: # probably no variables in the file
97
109
  continue
98
110
  for name, descriptions in existing_descriptions.items():
@@ -101,7 +113,7 @@ def update_block_descriptions(
101
113
  path=tf_file,
102
114
  name=name,
103
115
  before=description,
104
- after=new_names.get(name, ""),
116
+ after=get_description(name, description, tf_file),
105
117
  block_type=block_type,
106
118
  )
107
119
  for description in descriptions
@@ -1,8 +1,9 @@
1
+ from __future__ import annotations
1
2
  import logging
2
3
  from collections import defaultdict
3
4
  from copy import deepcopy
4
5
  from pathlib import Path
5
- from typing import Callable
6
+ from typing import Callable, Protocol
6
7
 
7
8
  import hcl2
8
9
  from lark import Token, Tree
@@ -40,14 +41,16 @@ def is_block_type(tree: Tree, block_type: str) -> bool:
40
41
  return False
41
42
 
42
43
 
43
- def update_description(tree: Tree, new_descriptions: dict[str, str], existing_names: dict[str, list[str]]) -> Tree:
44
+ def update_description(
45
+ path: Path, tree: Tree, get_new_description: NewDescription, existing_names: dict[str, list[str]]
46
+ ) -> Tree:
44
47
  new_children = tree.children.copy()
45
48
  variable_body = new_children[2]
46
49
  assert variable_body.data == "body"
47
50
  name = token_name(new_children[1])
48
51
  old_description = read_description_attribute(variable_body)
49
52
  existing_names[name].append(old_description)
50
- new_description = new_descriptions.get(name, "")
53
+ new_description = get_new_description(name, old_description, path)
51
54
  if not new_description:
52
55
  debug_log(f"no description found for variable {name}", 0)
53
56
  return tree
@@ -60,6 +63,8 @@ def token_name(token: Token | Tree) -> str:
60
63
  return token.value.strip('"')
61
64
  if isinstance(token, Tree) and token.data == "identifier":
62
65
  return token.children[0].value.strip('"') # type: ignore
66
+ if isinstance(token, Tree) and isinstance(token.data, Token) and token.data.value == "heredoc_template_trim":
67
+ return token.children[0].value.strip('"') # type: ignore
63
68
  err_msg = f"unexpected token type {type(token)} for token name"
64
69
  raise ValueError(err_msg)
65
70
 
@@ -103,10 +108,11 @@ def read_description_attribute(tree: Tree) -> str:
103
108
 
104
109
 
105
110
  def create_description_attribute(description_value: str) -> Tree:
111
+ token_value = f"<<-EOT\n{description_value}\nEOT\n" if "\n" in description_value else f'"{description_value}"'
106
112
  children = [
107
113
  Tree(Token("RULE", "identifier"), [Token("NAME", "description")]),
108
114
  Token("EQ", " ="),
109
- Tree(Token("RULE", "expr_term"), [Token("STRING_LIT", f'"{description_value}"')]),
115
+ Tree(Token("RULE", "expr_term"), [Token("STRING_LIT", token_value)]),
110
116
  ]
111
117
  return Tree(Token("RULE", "attribute"), children)
112
118
 
@@ -129,9 +135,14 @@ def process_generic(
129
135
  return Tree(node.data, new_children)
130
136
 
131
137
 
138
+ class NewDescription(Protocol):
139
+ def __call__(self, name: str, old_description: str, path: Path) -> str: ...
140
+
141
+
132
142
  def process_descriptions(
143
+ path: Path,
133
144
  node: Tree,
134
- name_updates: dict[str, str],
145
+ new_description: NewDescription,
135
146
  existing_names: dict[str, list[str]],
136
147
  depth=0,
137
148
  *,
@@ -141,7 +152,7 @@ def process_descriptions(
141
152
  return is_block_type(tree, block_type)
142
153
 
143
154
  def tree_call(tree: Tree) -> Tree:
144
- return update_description(tree, name_updates, existing_names)
155
+ return update_description(path, tree, new_description, existing_names)
145
156
 
146
157
  return process_generic(
147
158
  node,
@@ -151,14 +162,17 @@ def process_descriptions(
151
162
  )
152
163
 
153
164
 
154
- def update_descriptions(tf_path: Path, new_names: dict[str, str], block_type: str) -> tuple[str, dict[str, list[str]]]:
165
+ def update_descriptions(
166
+ tf_path: Path, new_description: NewDescription, block_type: str
167
+ ) -> tuple[str, dict[str, list[str]]]:
155
168
  tree = safe_parse(tf_path)
156
169
  if tree is None:
157
170
  return "", {}
158
171
  existing_descriptions = defaultdict(list)
159
172
  new_tree = process_descriptions(
173
+ tf_path,
160
174
  tree,
161
- new_names,
175
+ new_description,
162
176
  existing_descriptions,
163
177
  block_type=block_type,
164
178
  )
@@ -243,7 +243,9 @@ def detect_ambiguous_env_vars(manual_env_vars: dict[str, str]) -> list[str]:
243
243
 
244
244
  def find_missing_env_vars(required_env_vars: list[str], manual_env_vars: dict[str, str]) -> list[str]:
245
245
  return sorted(
246
- env_name for env_name in required_env_vars if read_from_env(env_name) == "" and env_name not in manual_env_vars
246
+ env_name
247
+ for env_name in required_env_vars
248
+ if read_from_env(env_name) == "" and env_name not in manual_env_vars and env_name
247
249
  )
248
250
 
249
251
 
@@ -255,7 +257,15 @@ def init_settings(
255
257
  profile_env_vars = settings.manual_env_vars
256
258
  vscode_env_vars = settings.env_vars_vs_code
257
259
  if vscode_env_vars.exists():
258
- profile_env_vars |= load_dotenv(vscode_env_vars)
260
+ skip_generated_vars: set[str] = set()
261
+ if "AWS_PROFILE" in profile_env_vars:
262
+ skip_generated_vars |= {
263
+ "AWS_ACCESS_KEY_ID",
264
+ "AWS_SECRET_ACCESS_KEY",
265
+ } # avoid generated env-vars overwriting AWS PROFILE
266
+ profile_env_vars |= {
267
+ key: value for key, value in load_dotenv(vscode_env_vars).items() if key not in skip_generated_vars
268
+ }
259
269
  required_env_vars = collect_required_env_vars(list(settings_classes))
260
270
  ambiguous = [] if skip_ambiguous_check else detect_ambiguous_env_vars(profile_env_vars)
261
271
  missing_env_vars = find_missing_env_vars(required_env_vars, profile_env_vars)
@@ -1,18 +1,18 @@
1
- from collections import defaultdict
2
- from concurrent.futures import Future, as_completed
3
- from functools import lru_cache
4
1
  import json
5
2
  import logging
6
3
  import os
4
+ from collections import defaultdict
5
+ from concurrent.futures import Future, as_completed
6
+ from functools import lru_cache
7
7
  from pathlib import Path
8
8
 
9
+ import requests
10
+ import typer
9
11
  from ask_shell import new_task, print_to_live, run_pool
10
12
  from model_lib import dump, parse_model
11
13
  from pydantic import BaseModel, Field, model_validator
12
- import requests
13
- from rich.markdown import Markdown
14
- import typer
15
14
  from requests.auth import HTTPDigestAuth
15
+ from rich.markdown import Markdown
16
16
  from zero_3rdparty.file_utils import ensure_parents_write_text
17
17
  from zero_3rdparty.str_utils import ensure_prefix, ensure_suffix, instance_repr
18
18
 
@@ -27,7 +27,7 @@ from atlas_init.settings.env_vars_modules import (
27
27
  TFModuleStream_Instance,
28
28
  )
29
29
  from atlas_init.settings.path import load_dotenv
30
- from atlas_init.tf_ext.settings import TfDepSettings
30
+ from atlas_init.tf_ext.settings import TfExtSettings
31
31
 
32
32
  logger = logging.getLogger(__name__)
33
33
 
@@ -259,7 +259,7 @@ def api_config(
259
259
  md_content = "\n".join(md_report)
260
260
  md = Markdown(md_content)
261
261
  print_to_live(md)
262
- output_path = TfDepSettings.from_env().pagination_output_path(query_args_str)
262
+ output_path = TfExtSettings.from_env().pagination_output_path(query_args_str)
263
263
  ensure_parents_write_text(output_path, md_content)
264
264
  logger.info(f"Pagination report saved to {output_path}")
265
265
  return md
@@ -288,7 +288,7 @@ def api(
288
288
 
289
289
 
290
290
  def dump_config_path(query_args: dict[str, str]) -> Path:
291
- settings = TfDepSettings.from_env()
291
+ settings = TfExtSettings.from_env()
292
292
  latest_api_spec = resolve_admin_api_path()
293
293
  model = parse_model(latest_api_spec, t=OpenapiSchema)
294
294
  paginated_paths: list[ApiCall] = []
@@ -0,0 +1,32 @@
1
+ import typer
2
+
3
+
4
+ def default_skippped_directories() -> list[str]:
5
+ return [
6
+ "prometheus-and-teams", # Provider registry.terraform.io/hashicorp/template v2.2.0 does not have a package available for your current platform, darwin_arm64.
7
+ ]
8
+
9
+
10
+ REPO_PATH_ATLAS_ARG = typer.Argument(..., help="Path to the mongodbatlas-terraform-provider repository")
11
+ SKIP_EXAMPLES_DIRS_OPTION = typer.Option(
12
+ ...,
13
+ "--skip-examples",
14
+ help="Skip example directories with these names",
15
+ default_factory=default_skippped_directories,
16
+ show_default=True,
17
+ )
18
+ TF_CLI_CONFIG_FILE_ENV_NAME = "TF_CLI_CONFIG_FILE"
19
+ TF_CLI_CONFIG_FILE_ARG = typer.Option(
20
+ "",
21
+ "-tf-cli",
22
+ "--tf-cli-config-file",
23
+ envvar=TF_CLI_CONFIG_FILE_ENV_NAME,
24
+ help="Terraform CLI config file",
25
+ )
26
+ ENV_NAME_REPO_PATH_ATLAS_PROVIDER = "REPO_PATH_ATLAS_PROVIDER"
27
+ TF_REPO_PATH_ATLAS = typer.Option(
28
+ "",
29
+ "--tf-repo-path-atlas",
30
+ help="Path to the mongodbatlas-terraform-provider repository",
31
+ envvar=ENV_NAME_REPO_PATH_ATLAS_PROVIDER,
32
+ )
@@ -0,0 +1,141 @@
1
+ from contextlib import suppress
2
+ from dataclasses import asdict
3
+ from functools import singledispatch
4
+ from pathlib import Path
5
+
6
+ from zero_3rdparty import humps
7
+ from zero_3rdparty.file_utils import clean_dir, ensure_parents_write_text
8
+
9
+ from atlas_init.tf_ext.gen_resource_variables import generate_resource_variables
10
+ from atlas_init.tf_ext.gen_versions import dump_versions_tf
11
+ from atlas_init.tf_ext.models_module import ModuleGenConfig, ResourceAbs, ResourceGenConfig, ResourceTypePythonModule
12
+ from atlas_init.tf_ext.py_gen import import_module_by_using_parents
13
+
14
+ VARIABLE_PLACEHOLDER = "var."
15
+ INDENT = " "
16
+
17
+
18
+ def _examples_casted(examples: dict) -> dict[str, ResourceAbs]:
19
+ return examples
20
+
21
+
22
+ def read_example_dirs(module_path: Path) -> list[Path]:
23
+ return sorted(
24
+ example_dir
25
+ for example_dir in (module_path / "examples").glob("*")
26
+ if example_dir.is_dir()
27
+ and len(example_dir.name) > 2
28
+ and example_dir.name[:2].isdigit()
29
+ and (example_dir / "main.tf").exists()
30
+ )
31
+
32
+
33
+ def generate_module_examples(
34
+ config: ModuleGenConfig,
35
+ module: ResourceTypePythonModule,
36
+ resource_type: str,
37
+ *,
38
+ skip_clean_dir: bool = False,
39
+ ) -> list[Path]:
40
+ test_path = config.examples_test_path
41
+ imported_module = import_module_by_using_parents(test_path)
42
+ examples = getattr(imported_module, "EXAMPLES")
43
+ assert isinstance(examples, dict), f"{imported_module} does not have an EXAMPLES attribute"
44
+ examples_parsed = _examples_casted(examples)
45
+ examples_generated: list[Path] = []
46
+ for example_name, example in examples_parsed.items():
47
+ dumped_resource = {k: v for k, v in asdict(example).items() if v is not None}
48
+ variables = {
49
+ k: f"{v}{k}"
50
+ for k, v in dumped_resource.items()
51
+ if isinstance(v, str) and v.startswith(VARIABLE_PLACEHOLDER)
52
+ }
53
+ dumped_resource |= variables
54
+ variable_names = set(variables.keys())
55
+ ignored_names = set(module.all_field_names) - variable_names
56
+ ignored_names |= module.all_skip_variables
57
+ resource_cls = module.resource_ext or module.resource
58
+ assert resource_cls, f"{module} does not have a resource class"
59
+ example_path = config.example_path(example_name)
60
+ if not skip_clean_dir and example_path.exists():
61
+ clean_dir(example_path)
62
+
63
+ variables_tf = generate_resource_variables(
64
+ resource_cls,
65
+ ResourceGenConfig(
66
+ name=resource_type, skip_variables_extra=ignored_names, required_variables=variable_names
67
+ ),
68
+ )
69
+ ensure_parents_write_text(example_path / "variables.tf", variables_tf)
70
+ variables_str = "\n".join(f"{k} = {dump_variable(v)}" for k, v in dumped_resource.items() if can_dump(v))
71
+ example_main = example_main_tf(config, variables_str)
72
+ ensure_parents_write_text(example_path / "main.tf", example_main)
73
+ dump_versions_tf(example_path, skip_python=config.skip_python, minimal=True)
74
+ examples_generated.append(example_path)
75
+ return examples_generated
76
+
77
+
78
+ class NotDumpableError(Exception):
79
+ def __init__(self, value: object) -> None:
80
+ super().__init__(f"Cannot dump variable {value!r}")
81
+
82
+
83
+ def can_dump(variable: object) -> bool:
84
+ with suppress(NotDumpableError):
85
+ dump_variable(variable)
86
+ return True
87
+ return False
88
+
89
+
90
+ @singledispatch
91
+ def dump_variable(variable: object) -> str:
92
+ raise NotDumpableError(variable)
93
+
94
+
95
+ @dump_variable.register
96
+ def dump_variable_str(variable: str) -> str:
97
+ if "." in variable:
98
+ return variable
99
+ return f'"{variable}"'
100
+
101
+
102
+ @dump_variable.register
103
+ def dump_variable_int(variable: int) -> str:
104
+ return str(variable)
105
+
106
+
107
+ @dump_variable.register
108
+ def dump_variable_float(variable: float) -> str:
109
+ return str(variable)
110
+
111
+
112
+ @dump_variable.register
113
+ def dump_variable_bool(variable: bool) -> str:
114
+ return "true" if variable else "false"
115
+
116
+
117
+ @dump_variable.register
118
+ def dump_variable_list(variable: list) -> str:
119
+ return f"[\n{', '.join(f'{INDENT}{dump_variable(nested)}' for nested in variable if can_dump(nested))}\n]"
120
+
121
+
122
+ @dump_variable.register
123
+ def dump_variable_set(variable: set) -> str:
124
+ return f"[\n{', '.join(f'{INDENT}{dump_variable(nested)}' for nested in variable if can_dump(nested))}\n]"
125
+
126
+
127
+ @dump_variable.register
128
+ def dump_variable_dict(variable: dict) -> str:
129
+ return "{\n" + "\n".join(f"{INDENT}{k} = {dump_variable(v)}" for k, v in variable.items() if can_dump(v)) + "\n}"
130
+
131
+
132
+ def example_main_tf(module: ModuleGenConfig, variables: str) -> str:
133
+ variables_indented = "\n".join(f"{INDENT}{var}" for var in variables.split("\n"))
134
+ module_name_snake = humps.dekebabize(module.name)
135
+ return f"""\
136
+ module "{module_name_snake}" {{
137
+ source = "../.."
138
+
139
+ {variables_indented}
140
+ }}
141
+ """
@@ -0,0 +1,131 @@
1
+ import logging
2
+ from enum import StrEnum
3
+
4
+ from ask_shell import run_and_wait
5
+ from zero_3rdparty.file_utils import ensure_parents_write_text, update_between_markers
6
+
7
+ from atlas_init.tf_ext.gen_examples import read_example_dirs
8
+ from atlas_init.tf_ext.models_module import ModuleGenConfig
9
+
10
+ logger = logging.getLogger(__name__)
11
+ _readme_disclaimer = """\
12
+ ## Disclaimer
13
+ This Module is not meant for external consumption.
14
+ It is part of a development PoC.
15
+ Any usage problems will not be supported.
16
+ However, if you have any ideas or feedback feel free to open a Github Issue!
17
+ """
18
+
19
+
20
+ class ReadmeMarkers(StrEnum):
21
+ DISCLAIMER = "DISCLAIMER"
22
+ EXAMPLE = "TF_EXAMPLES"
23
+ TF_DOCS = "TF_DOCS"
24
+
25
+ @classmethod
26
+ def as_start(cls, marker_name: str) -> str:
27
+ return f"<!-- BEGIN_{marker_name} -->"
28
+
29
+ @classmethod
30
+ def as_end(cls, marker_name: str) -> str:
31
+ return f"<!-- END_{marker_name} -->"
32
+
33
+ @classmethod
34
+ def marker_lines(cls, marker_name: str) -> str:
35
+ return f"""\
36
+ {cls.as_start(marker_name)}
37
+
38
+ {cls.as_end(marker_name)}
39
+ """
40
+
41
+ @classmethod
42
+ def example_boilerplate(cls) -> str:
43
+ return "\n".join(cls.marker_lines(marker_name) for marker_name in list(cls))
44
+
45
+
46
+ def read_examples(module: ModuleGenConfig) -> str:
47
+ example_dirs = read_example_dirs(module.module_out_path)
48
+ content = ["# Examples"]
49
+ for example_dir in example_dirs:
50
+ example_name = example_dir.name
51
+ header_name = example_name.replace("_", " ").replace("-", " ").title()
52
+ main_path = example_dir / "main.tf"
53
+ assert main_path.exists(), f"{main_path} does not exist, every example must have a main.tf"
54
+ content.extend(
55
+ [
56
+ f"## [{header_name}](./examples/{example_name})",
57
+ "",
58
+ "```terraform",
59
+ main_path.read_text(),
60
+ "```",
61
+ "",
62
+ "",
63
+ ]
64
+ )
65
+ return "\n".join(content)
66
+
67
+
68
+ _static_terraform_config = """\
69
+ formatter: markdown document
70
+ output:
71
+ file: "FILENAME"
72
+ mode: inject
73
+ template: |-
74
+ START_MARKER
75
+ {{ .Content }}
76
+ END_MARKER
77
+ sort:
78
+ enabled: true
79
+ by: required
80
+ """
81
+
82
+
83
+ def terraform_docs_config_content(module: ModuleGenConfig) -> str:
84
+ config = _static_terraform_config
85
+ for replacement_in, replacement_out in [
86
+ ("FILENAME", module.readme_path().name),
87
+ ("START_MARKER", ReadmeMarkers.as_start(ReadmeMarkers.TF_DOCS)),
88
+ ("END_MARKER", ReadmeMarkers.as_end(ReadmeMarkers.TF_DOCS)),
89
+ ]:
90
+ config = config.replace(replacement_in, replacement_out)
91
+ return config
92
+
93
+
94
+ def generate_readme(module: ModuleGenConfig) -> str:
95
+ readme_path = module.readme_path()
96
+ assert readme_path.exists(), (
97
+ f"{readme_path} does not exist, currently a boilerplate is expected, consider adding to {readme_path}\n{ReadmeMarkers.example_boilerplate()}"
98
+ )
99
+ update_between_markers(
100
+ readme_path,
101
+ _readme_disclaimer,
102
+ ReadmeMarkers.as_start(ReadmeMarkers.DISCLAIMER),
103
+ ReadmeMarkers.as_end(ReadmeMarkers.DISCLAIMER),
104
+ )
105
+ run_and_wait("terraform fmt -recursive .", cwd=module.module_out_path, allow_non_zero_exit=True, ansi_content=False)
106
+ example_section = read_examples(module)
107
+ update_between_markers(
108
+ readme_path,
109
+ example_section,
110
+ ReadmeMarkers.as_start(ReadmeMarkers.EXAMPLE),
111
+ ReadmeMarkers.as_end(ReadmeMarkers.EXAMPLE),
112
+ )
113
+ docs_config_path = module.terraform_docs_config_path()
114
+ if docs_config_path.exists():
115
+ logger.warning(f"{docs_config_path} already exists, skipping generation")
116
+ else:
117
+ config_content = terraform_docs_config_content(module)
118
+ ensure_parents_write_text(docs_config_path, config_content)
119
+ logger.info(f"generated {docs_config_path}")
120
+ run_and_wait(f"terraform-docs -c {docs_config_path} .", cwd=module.module_out_path)
121
+ readme_content = _default_link_updater(readme_path.read_text())
122
+ ensure_parents_write_text(readme_path, readme_content)
123
+ return readme_path.read_text()
124
+
125
+
126
+ def _default_link_updater(readme_content: str) -> str: # can be a global replacer for now
127
+ for replace_in, replace_out in {
128
+ "docs/resources/advanced_cluster": r"docs/resources/advanced_cluster%2520%2528preview%2520provider%25202.0.0%2529"
129
+ }.items():
130
+ readme_content = readme_content.replace(replace_in, replace_out)
131
+ return readme_content