atlas-init 0.8.1__tar.gz → 0.10.0__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.
- {atlas_init-0.8.1 → atlas_init-0.10.0}/PKG-INFO +2 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/__init__.py +1 -1
- atlas_init-0.10.0/atlas_init/cli_root/aws_clean.py +108 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_root/trigger.py +1 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/modifier2.py +51 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/rich_utils.py +22 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/gen_examples.py +4 -2
- atlas_init-0.8.1/atlas_init/tf_ext/gen_module_readme.py → atlas_init-0.10.0/atlas_init/tf_ext/gen_readme.py +46 -26
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/models.py +27 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/models_module.py +7 -3
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/paths.py +18 -1
- atlas_init-0.10.0/atlas_init/tf_ext/run_tf.py +20 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/settings.py +8 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_dep.py +69 -22
- atlas_init-0.10.0/atlas_init/tf_ext/tf_example_readme.py +392 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_mod_gen.py +8 -25
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_modules.py +3 -1
- atlas_init-0.10.0/atlas_init/tf_ext/tf_ws.py +269 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/typer_app.py +3 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/typer_app.py +2 -1
- {atlas_init-0.8.1 → atlas_init-0.10.0}/pyproject.toml +1 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/.gitignore +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/LICENSE +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/__main__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/atlas_init.yaml +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_args.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/app.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/aws.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/cfn_parameter_finder.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/contract.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/example.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_cfn/files.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/go.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/run.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/run_manager.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/sdk.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/sdk_auto_changes.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_helper/tf_runner.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_root/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_root/go_test.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_root/mms_released.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/app.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/changelog.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/ci_tests.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/codegen/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/codegen/models.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/codegen/openapi_minimal.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/debug_logs.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/debug_logs_test_data.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/debug_logs_test_data_package_config.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/example_update.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/github_logs.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/go_test_run.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/go_test_summary.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/go_test_tf_error.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/cli.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/cluster_mig.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/modifier.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/hcl/parser.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/log_clean.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/mock_tf_log.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/openapi.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_go_parser.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_inspection.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_table.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_table_models.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v2.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v2_sdk.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v3.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v3_sdk.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v3_sdk_base.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cli_tf/schema_v3_sdk_create.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cloud/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/cloud/aws.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/crud/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/crud/mongo_client.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/crud/mongo_dao.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/crud/mongo_utils.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/html_out/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/html_out/md_export.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/humps.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/repos/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/repos/cfn.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/repos/go_sdk.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/repos/path.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/sdk_ext/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/sdk_ext/go.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/sdk_ext/typer_app.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/config.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/env_vars.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/env_vars_generated.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/env_vars_modules.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/interactive.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/interactive2.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/settings/path.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/terraform.yaml +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/.terraform.lock.hcl +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/always.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/main.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_kms/aws_kms.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_kms/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_s3/aws_s3.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_s3/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_vars/aws_vars.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_vpc/aws_vpc.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/aws_vpc/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/assume_role_services.yaml +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/cfn.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/kms.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/resource_actions.yaml +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cfn/variables.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cloud_provider/cloud_provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cloud_provider/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cluster/cluster.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/cluster/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/encryption_at_rest/main.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/encryption_at_rest/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/federated_vars/federated_vars.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/federated_vars/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/project_extra/project_extra.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/project_extra/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/stream_instance/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/stream_instance/stream_instance.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/vpc_peering/provider.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/vpc_peering/vpc_peering.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/vpc_privatelink/atlas-privatelink.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/vpc_privatelink/variables.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/modules/vpc_privatelink/versions.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/outputs.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/providers.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf/variables.tf +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/__init__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/__main__.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/api_call.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/args.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/constants.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/gen_resource_main.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/gen_resource_output.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/gen_resource_variables.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/gen_versions.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/newres.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/plan_diffs.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/provider_schema.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/py_gen.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/schema_to_dataclass.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_desc_gen.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_desc_update.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_mod_gen_provider.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/atlas_init/tf_ext/tf_vars.py +0 -0
- {atlas_init-0.8.1 → atlas_init-0.10.0}/readme.md +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: atlas-init
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.10.0
|
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
|
@@ -20,6 +20,7 @@ Requires-Dist: inflection==0.5.1
|
|
20
20
|
Requires-Dist: model-lib
|
21
21
|
Requires-Dist: motor==3.7.1
|
22
22
|
Requires-Dist: mypy-boto3-cloudformation==1.37.22
|
23
|
+
Requires-Dist: mypy-boto3-iam>=1.40.0
|
23
24
|
Requires-Dist: orjson==3.10.13
|
24
25
|
Requires-Dist: pydantic-settings==2.7.1
|
25
26
|
Requires-Dist: pydot==4.0.1
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import logging
|
2
|
+
from datetime import datetime, timedelta
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
import humanize
|
6
|
+
import typer
|
7
|
+
from ask_shell import run_and_wait
|
8
|
+
from boto3.session import Session
|
9
|
+
from model_lib import Entity
|
10
|
+
from mypy_boto3_iam import IAMClient
|
11
|
+
from mypy_boto3_iam.type_defs import RoleTypeDef
|
12
|
+
from zero_3rdparty.datetime_utils import utc_now
|
13
|
+
|
14
|
+
from atlas_init.cloud.aws import PascalAlias
|
15
|
+
from atlas_init.settings.env_vars import init_settings
|
16
|
+
from atlas_init.typer_app import app_command
|
17
|
+
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
|
21
|
+
class LastUsed(Entity):
|
22
|
+
model_config = PascalAlias
|
23
|
+
last_used_date: datetime
|
24
|
+
|
25
|
+
|
26
|
+
class IAMRole(Entity):
|
27
|
+
model_config = PascalAlias
|
28
|
+
arn: str
|
29
|
+
role_name: str
|
30
|
+
create_date: datetime
|
31
|
+
role_last_used: LastUsed | None = None
|
32
|
+
|
33
|
+
|
34
|
+
@app_command()
|
35
|
+
def aws_clean(
|
36
|
+
skip_iam_roles: bool = typer.Option(False, help="skip iam roles"),
|
37
|
+
iam_role_prefix_name: str = typer.Option(
|
38
|
+
"mongodb-atlas-test-acc-tf-",
|
39
|
+
help="prefix name of iam roles to clean",
|
40
|
+
),
|
41
|
+
):
|
42
|
+
init_settings()
|
43
|
+
if skip_iam_roles:
|
44
|
+
return
|
45
|
+
client: IAMClient = Session().client("iam") # pyright: ignore[reportAssignmentType]
|
46
|
+
all_roles: list[RoleTypeDef] = []
|
47
|
+
aws_account_id = run_and_wait("aws sts get-caller-identity --query Account --output text").stdout_one_line
|
48
|
+
|
49
|
+
roles_response = client.list_roles()
|
50
|
+
|
51
|
+
all_roles.extend(roles_response["Roles"])
|
52
|
+
marker = roles_response.get("Marker", "")
|
53
|
+
while marker:
|
54
|
+
roles_response = client.list_roles(Marker=marker)
|
55
|
+
all_roles.extend(roles_response["Roles"])
|
56
|
+
marker = roles_response.get("Marker", "")
|
57
|
+
total_roles = len(all_roles)
|
58
|
+
logger.info(f"found {total_roles} roles")
|
59
|
+
roles_parsed: list[IAMRole] = []
|
60
|
+
delete_if_created_before = utc_now() - timedelta(days=5)
|
61
|
+
delete_count = 0
|
62
|
+
role_names: list[str] = []
|
63
|
+
for role in all_roles:
|
64
|
+
parsed = IAMRole.model_validate(role)
|
65
|
+
roles_parsed.append(parsed)
|
66
|
+
role_name = parsed.role_name
|
67
|
+
role_names.append(role_name)
|
68
|
+
if not role_name.startswith(iam_role_prefix_name):
|
69
|
+
continue
|
70
|
+
# want to delete 'mongodb-atlas-test-acc-tf-1345851232260229574'
|
71
|
+
# want to keep 'mongodb-atlas-test-acc-tf-7973337217371171538-git-ear'?
|
72
|
+
if role_name[-1].isdigit() and parsed.create_date < delete_if_created_before:
|
73
|
+
logger.info(f"role: {parsed.arn} will be deleted")
|
74
|
+
delete_count += 1
|
75
|
+
delete_role(client, role_name)
|
76
|
+
else:
|
77
|
+
logger.info(f"skipping role: {parsed.arn}, created: {humanize.naturaltime(parsed.create_date)}")
|
78
|
+
logger.info(f"deleted {delete_count}/{total_roles} roles")
|
79
|
+
out_path = Path(f"aws_roles_{aws_account_id}.txt")
|
80
|
+
out_path.write_text("\n".join(sorted(role_names)))
|
81
|
+
|
82
|
+
|
83
|
+
def delete_role(client: IAMClient, role_name: str):
|
84
|
+
try:
|
85
|
+
attached_policies = client.list_attached_role_policies(RoleName=role_name)
|
86
|
+
for policy in attached_policies["AttachedPolicies"]:
|
87
|
+
policy_arn = policy.get("PolicyArn")
|
88
|
+
if policy_arn:
|
89
|
+
logger.info(f"detaching managed policy: {policy_arn} from role: {role_name}")
|
90
|
+
client.detach_role_policy(RoleName=role_name, PolicyArn=policy_arn)
|
91
|
+
except Exception as e:
|
92
|
+
logger.warning(f"failed to detach managed policies from role {role_name}: {e}")
|
93
|
+
|
94
|
+
# Second, delete all inline policies
|
95
|
+
try:
|
96
|
+
inline_policies = client.list_role_policies(RoleName=role_name)
|
97
|
+
for policy_name in inline_policies["PolicyNames"]:
|
98
|
+
logger.info(f"deleting inline policy: {policy_name} from role: {role_name}")
|
99
|
+
client.delete_role_policy(RoleName=role_name, PolicyName=policy_name)
|
100
|
+
except Exception as e:
|
101
|
+
logger.warning(f"failed to delete inline policies from role {role_name}: {e}")
|
102
|
+
|
103
|
+
# Finally, delete the role
|
104
|
+
try:
|
105
|
+
client.delete_role(RoleName=role_name)
|
106
|
+
logger.info(f"deleted role: {role_name}")
|
107
|
+
except Exception as e:
|
108
|
+
logger.error(f"failed to delete role {role_name}: {e}")
|
@@ -30,7 +30,7 @@ def create_realm_app():
|
|
30
30
|
project_id = atlas_settings.MONGODB_ATLAS_PROJECT_ID
|
31
31
|
base_url = atlas_settings.realm_url
|
32
32
|
cluster_name = cluster_settings.MONGODB_ATLAS_CLUSTER_NAME
|
33
|
-
auth_headers = login_to_realm(
|
33
|
+
auth_headers = login_to_realm(atlas_settings, base_url)
|
34
34
|
realm_settings = env_vars_cls_or_none(RealmSettings, dotenv_path=settings.env_vars_trigger)
|
35
35
|
if realm_settings and function_exists(
|
36
36
|
base_url,
|
@@ -2,10 +2,12 @@ from collections import defaultdict
|
|
2
2
|
import logging
|
3
3
|
from contextlib import suppress
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import NamedTuple
|
5
|
+
from typing import Any, NamedTuple
|
6
6
|
from lark import Token, Transformer, Tree, UnexpectedToken, v_args
|
7
7
|
from hcl2.transformer import Attribute, DictTransformer
|
8
8
|
from hcl2.api import reverse_transform, writes, parses
|
9
|
+
from model_lib import Entity
|
10
|
+
from pydantic import field_validator
|
9
11
|
import rich
|
10
12
|
|
11
13
|
logger = logging.getLogger(__name__)
|
@@ -58,6 +60,54 @@ def attribute_transfomer(attr_name: str, obj_key: str, new_value: str) -> tuple[
|
|
58
60
|
return AttributeTransformer(with_meta=True), changes
|
59
61
|
|
60
62
|
|
63
|
+
_unset = object()
|
64
|
+
|
65
|
+
|
66
|
+
class TFVar(Entity):
|
67
|
+
name: str
|
68
|
+
description: str | None = ""
|
69
|
+
default: Any = _unset
|
70
|
+
type: str = ""
|
71
|
+
sensitive: bool = False
|
72
|
+
|
73
|
+
@field_validator("default", mode="before")
|
74
|
+
def unpack_token(cls, v: Any) -> Any:
|
75
|
+
if isinstance(v, Token):
|
76
|
+
return v.value.strip('"')
|
77
|
+
return v
|
78
|
+
|
79
|
+
|
80
|
+
def variable_reader_typed(tree: Tree) -> dict[str, TFVar]:
|
81
|
+
variables: dict[str, TFVar] = {}
|
82
|
+
|
83
|
+
class TFVarReader(DictTransformer):
|
84
|
+
def __init__(self, with_meta: bool = False, *, name: str):
|
85
|
+
super().__init__(with_meta)
|
86
|
+
self.kwargs: dict[str, Any] = {
|
87
|
+
"name": name,
|
88
|
+
}
|
89
|
+
|
90
|
+
def attribute(self, args: list) -> Attribute:
|
91
|
+
if len(args) == 3:
|
92
|
+
name, _, value = args
|
93
|
+
self.kwargs[name] = value
|
94
|
+
return super().attribute(args)
|
95
|
+
|
96
|
+
class BlockReader(Transformer):
|
97
|
+
@v_args(tree=True)
|
98
|
+
def block(self, block_tree: Tree) -> Tree:
|
99
|
+
current_block_name = _identifier_name(block_tree)
|
100
|
+
if current_block_name == "variable":
|
101
|
+
variable_name = token_name(block_tree.children[1])
|
102
|
+
reader = TFVarReader(name=variable_name)
|
103
|
+
reader.transform(block_tree)
|
104
|
+
variables[variable_name] = TFVar(**reader.kwargs)
|
105
|
+
return block_tree
|
106
|
+
|
107
|
+
BlockReader().transform(tree)
|
108
|
+
return variables
|
109
|
+
|
110
|
+
|
61
111
|
def variable_reader(tree: Tree) -> dict[str, str | None]:
|
62
112
|
"""
|
63
113
|
Reads the variable names from a parsed HCL2 tree.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Literal
|
3
3
|
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.tree import Tree
|
4
6
|
import typer
|
5
7
|
from pydantic import BaseModel
|
6
8
|
from rich.logging import RichHandler
|
@@ -62,3 +64,23 @@ def configure_logging(
|
|
62
64
|
app.pretty_exceptions_show_locals = False
|
63
65
|
|
64
66
|
return handler
|
67
|
+
|
68
|
+
|
69
|
+
# https://github.com/Textualize/rich/blob/8c4d3d1d50047e3aaa4140d0ffc1e0c9f1df5af4/tests/test_live.py#L11
|
70
|
+
def create_capture_console(*, width: int = 60, height: int = 80, force_terminal: bool = True) -> Console:
|
71
|
+
return Console(
|
72
|
+
width=width,
|
73
|
+
height=height,
|
74
|
+
force_terminal=force_terminal,
|
75
|
+
legacy_windows=False,
|
76
|
+
color_system=None, # use no color system to reduce complexity of output,
|
77
|
+
_environ={},
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
def tree_text(tree: Tree) -> str:
|
82
|
+
console = create_capture_console()
|
83
|
+
console.width = 10_000
|
84
|
+
console.begin_capture()
|
85
|
+
console.print(tree)
|
86
|
+
return "\n".join(line.rstrip() for line in console.end_capture().splitlines())
|
@@ -19,10 +19,12 @@ def _examples_casted(examples: dict) -> dict[str, ResourceAbs]:
|
|
19
19
|
return examples
|
20
20
|
|
21
21
|
|
22
|
-
def read_example_dirs(
|
22
|
+
def read_example_dirs(examples_dir: Path) -> list[Path]:
|
23
|
+
if not examples_dir.exists():
|
24
|
+
return []
|
23
25
|
return sorted(
|
24
26
|
example_dir
|
25
|
-
for example_dir in
|
27
|
+
for example_dir in examples_dir.glob("*")
|
26
28
|
if example_dir.is_dir()
|
27
29
|
and len(example_dir.name) > 2
|
28
30
|
and example_dir.name[:2].isdigit()
|
@@ -1,11 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
import logging
|
2
3
|
from enum import StrEnum
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Callable, TypeAlias
|
3
6
|
|
4
7
|
from ask_shell import run_and_wait
|
5
8
|
from zero_3rdparty.file_utils import ensure_parents_write_text, update_between_markers
|
6
9
|
|
7
10
|
from atlas_init.tf_ext.gen_examples import read_example_dirs
|
8
|
-
from atlas_init.tf_ext.models_module import
|
11
|
+
from atlas_init.tf_ext.models_module import EXAMPLES_DIRNAME, README_FILENAME, TERRAFORM_DOCS_CONFIG_FILENAME
|
9
12
|
|
10
13
|
logger = logging.getLogger(__name__)
|
11
14
|
_readme_disclaimer = """\
|
@@ -13,12 +16,13 @@ _readme_disclaimer = """\
|
|
13
16
|
This Module is not meant for external consumption.
|
14
17
|
It is part of a development PoC.
|
15
18
|
Any usage problems will not be supported.
|
16
|
-
However, if you have any ideas or feedback feel free to open a Github Issue!
|
19
|
+
However, if you have any ideas or feedback, feel free to open a Github Issue!
|
17
20
|
"""
|
18
21
|
|
19
22
|
|
20
23
|
class ReadmeMarkers(StrEnum):
|
21
24
|
DISCLAIMER = "DISCLAIMER"
|
25
|
+
MODULES = "MODULES"
|
22
26
|
EXAMPLE = "TF_EXAMPLES"
|
23
27
|
TF_DOCS = "TF_DOCS"
|
24
28
|
|
@@ -42,9 +46,23 @@ class ReadmeMarkers(StrEnum):
|
|
42
46
|
def example_boilerplate(cls) -> str:
|
43
47
|
return "\n".join(cls.marker_lines(marker_name) for marker_name in list(cls))
|
44
48
|
|
49
|
+
@classmethod
|
50
|
+
def readme_generators(cls) -> ReadmeGenerators:
|
51
|
+
return [
|
52
|
+
(cls.DISCLAIMER, lambda _: _readme_disclaimer),
|
53
|
+
(cls.EXAMPLE, lambda workspace: read_examples(workspace / EXAMPLES_DIRNAME)),
|
54
|
+
]
|
55
|
+
|
56
|
+
|
57
|
+
ReadmeGenerators: TypeAlias = list[tuple[ReadmeMarkers, Callable[[Path], str]]]
|
45
58
|
|
46
|
-
|
47
|
-
|
59
|
+
|
60
|
+
def read_examples(examples_dir: Path) -> str:
|
61
|
+
example_dirs = read_example_dirs(examples_dir)
|
62
|
+
if not example_dirs:
|
63
|
+
return ""
|
64
|
+
# ensure the examples are formatted first
|
65
|
+
run_and_wait("terraform fmt -recursive .", cwd=examples_dir.parent, allow_non_zero_exit=True, ansi_content=False)
|
48
66
|
content = ["# Examples"]
|
49
67
|
for example_dir in example_dirs:
|
50
68
|
example_name = example_dir.name
|
@@ -80,10 +98,10 @@ sort:
|
|
80
98
|
"""
|
81
99
|
|
82
100
|
|
83
|
-
def terraform_docs_config_content(
|
101
|
+
def terraform_docs_config_content(readme_path: Path) -> str:
|
84
102
|
config = _static_terraform_config
|
85
103
|
for replacement_in, replacement_out in [
|
86
|
-
("FILENAME",
|
104
|
+
("FILENAME", readme_path.name),
|
87
105
|
("START_MARKER", ReadmeMarkers.as_start(ReadmeMarkers.TF_DOCS)),
|
88
106
|
("END_MARKER", ReadmeMarkers.as_end(ReadmeMarkers.TF_DOCS)),
|
89
107
|
]:
|
@@ -91,36 +109,38 @@ def terraform_docs_config_content(module: ModuleGenConfig) -> str:
|
|
91
109
|
return config
|
92
110
|
|
93
111
|
|
94
|
-
def
|
95
|
-
|
112
|
+
def generate_and_write_readme(terraform_workdir: Path, *, generators: ReadmeGenerators | None = None) -> str:
|
113
|
+
generators = generators or ReadmeMarkers.readme_generators()
|
114
|
+
readme_path = terraform_workdir / README_FILENAME
|
96
115
|
assert readme_path.exists(), (
|
97
116
|
f"{readme_path} does not exist, currently a boilerplate is expected, consider adding to {readme_path}\n{ReadmeMarkers.example_boilerplate()}"
|
98
117
|
)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
118
|
+
for marker, generator in generators:
|
119
|
+
content = generator(terraform_workdir)
|
120
|
+
if not content:
|
121
|
+
continue
|
122
|
+
update_between_markers(
|
123
|
+
readme_path,
|
124
|
+
content,
|
125
|
+
ReadmeMarkers.as_start(marker),
|
126
|
+
ReadmeMarkers.as_end(marker),
|
127
|
+
)
|
128
|
+
generate_terraform_docs(readme_path)
|
129
|
+
logger.info(f"updated {readme_path}")
|
130
|
+
return readme_path.read_text()
|
131
|
+
|
132
|
+
|
133
|
+
def generate_terraform_docs(readme_path: Path) -> None:
|
134
|
+
docs_config_path = readme_path.parent / TERRAFORM_DOCS_CONFIG_FILENAME
|
114
135
|
if docs_config_path.exists():
|
115
136
|
logger.warning(f"{docs_config_path} already exists, skipping generation")
|
116
137
|
else:
|
117
|
-
config_content = terraform_docs_config_content(
|
138
|
+
config_content = terraform_docs_config_content(readme_path)
|
118
139
|
ensure_parents_write_text(docs_config_path, config_content)
|
119
140
|
logger.info(f"generated {docs_config_path}")
|
120
|
-
run_and_wait(f"terraform-docs -c {docs_config_path} .", cwd=
|
141
|
+
run_and_wait(f"terraform-docs -c {docs_config_path} .", cwd=readme_path.parent)
|
121
142
|
readme_content = _default_link_updater(readme_path.read_text())
|
122
143
|
ensure_parents_write_text(readme_path, readme_content)
|
123
|
-
return readme_path.read_text()
|
124
144
|
|
125
145
|
|
126
146
|
def _default_link_updater(readme_content: str) -> str: # can be a global replacer for now
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Iterable, Self
|
3
4
|
|
4
5
|
from model_lib import Entity
|
5
6
|
from pydantic import Field, RootModel, model_validator
|
@@ -30,6 +31,31 @@ def choose_next_emoji() -> str:
|
|
30
31
|
return emoji
|
31
32
|
|
32
33
|
|
34
|
+
@dataclass
|
35
|
+
class EmojiCounter:
|
36
|
+
counter: int = 0
|
37
|
+
existing_emojis: dict[str, str] = field(default_factory=dict)
|
38
|
+
|
39
|
+
@property
|
40
|
+
def emoji_to_names(self) -> dict[str, str]:
|
41
|
+
return dict(zip(self.existing_emojis.values(), self.existing_emojis.keys()))
|
42
|
+
|
43
|
+
def emoji_name(self) -> Iterable[tuple[str, str]]:
|
44
|
+
src = self.emoji_to_names
|
45
|
+
for emoji in _emojii_list:
|
46
|
+
if emoji in src:
|
47
|
+
yield emoji, src[emoji]
|
48
|
+
else:
|
49
|
+
break
|
50
|
+
|
51
|
+
def get_emoji(self, name: str) -> str:
|
52
|
+
if existing := self.existing_emojis.get(name):
|
53
|
+
return existing
|
54
|
+
emoji = self.existing_emojis[name] = _emojii_list[self.counter]
|
55
|
+
self.counter += 1
|
56
|
+
return emoji
|
57
|
+
|
58
|
+
|
33
59
|
class ModuleState(Entity):
|
34
60
|
resource_types: set[str] = Field(default_factory=set, description="Set of resource types in the module.")
|
35
61
|
|
@@ -24,6 +24,9 @@ from atlas_init.tf_ext.py_gen import (
|
|
24
24
|
from atlas_init.tf_ext.settings import RepoOut, TfExtSettings
|
25
25
|
|
26
26
|
ResourceTypeT: TypeAlias = str
|
27
|
+
TERRAFORM_DOCS_CONFIG_FILENAME: str = ".terraform-docs.yml"
|
28
|
+
README_FILENAME: str = "README.md"
|
29
|
+
EXAMPLES_DIRNAME: str = "examples"
|
27
30
|
|
28
31
|
|
29
32
|
@dataclass
|
@@ -253,12 +256,13 @@ class ModuleGenConfig(Entity):
|
|
253
256
|
return resource_type
|
254
257
|
raise ValueError(f"Could not resolve resource type for path {path}")
|
255
258
|
|
259
|
+
@property
|
256
260
|
def readme_path(self) -> Path:
|
257
|
-
return self.module_out_path /
|
261
|
+
return self.module_out_path / README_FILENAME
|
258
262
|
|
259
263
|
@property
|
260
264
|
def examples_path(self) -> Path:
|
261
|
-
return self.module_out_path /
|
265
|
+
return self.module_out_path / EXAMPLES_DIRNAME
|
262
266
|
|
263
267
|
def example_name(self, name: str, example_nr: int) -> str:
|
264
268
|
return f"{example_nr:02d}_{name}"
|
@@ -267,7 +271,7 @@ class ModuleGenConfig(Entity):
|
|
267
271
|
return self.examples_path / name
|
268
272
|
|
269
273
|
def terraform_docs_config_path(self) -> Path:
|
270
|
-
return self.module_out_path /
|
274
|
+
return self.module_out_path / TERRAFORM_DOCS_CONFIG_FILENAME
|
271
275
|
|
272
276
|
|
273
277
|
@dataclass
|
@@ -9,7 +9,14 @@ from model_lib import Entity
|
|
9
9
|
from pydantic import Field, RootModel
|
10
10
|
from zero_3rdparty.file_utils import iter_paths
|
11
11
|
|
12
|
-
from atlas_init.cli_tf.hcl.modifier2 import
|
12
|
+
from atlas_init.cli_tf.hcl.modifier2 import (
|
13
|
+
TFVar,
|
14
|
+
resource_types_vars_usage,
|
15
|
+
safe_parse,
|
16
|
+
variable_reader,
|
17
|
+
variable_reader_typed,
|
18
|
+
variable_usages,
|
19
|
+
)
|
13
20
|
from atlas_init.tf_ext.constants import ATLAS_PROVIDER_NAME, DEFAULT_EXTERNAL_SUBSTRINGS, DEFAULT_INTERNAL_SUBSTRINGS
|
14
21
|
|
15
22
|
logger = logging.getLogger(__name__)
|
@@ -32,6 +39,16 @@ def get_example_directories(repo_path: Path, skip_names: list[str]):
|
|
32
39
|
return example_dirs
|
33
40
|
|
34
41
|
|
42
|
+
def find_variables_typed(variables_tf: Path) -> dict[str, TFVar]:
|
43
|
+
if not variables_tf.exists():
|
44
|
+
return {}
|
45
|
+
tree = safe_parse(variables_tf)
|
46
|
+
if not tree:
|
47
|
+
logger.warning(f"Failed to parse {variables_tf}")
|
48
|
+
return {}
|
49
|
+
return variable_reader_typed(tree)
|
50
|
+
|
51
|
+
|
35
52
|
def find_variables(variables_tf: Path) -> dict[str, str | None]:
|
36
53
|
if not variables_tf.exists():
|
37
54
|
return {}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from ask_shell import new_task, run_and_wait
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
|
5
|
+
def validate_tf_workspace(
|
6
|
+
tf_workdir: Path, *, tf_cli_config_file: Path | None = None, env_extra: dict[str, str] | None = None
|
7
|
+
):
|
8
|
+
terraform_commands = [
|
9
|
+
"terraform init",
|
10
|
+
"terraform fmt .",
|
11
|
+
"terraform validate .",
|
12
|
+
]
|
13
|
+
env_extra = env_extra or {}
|
14
|
+
if tf_cli_config_file:
|
15
|
+
env_extra["TF_CLI_CONFIG_FILE"] = str(tf_cli_config_file)
|
16
|
+
with new_task("Terraform Module Validate Checks", total=len(terraform_commands)) as task:
|
17
|
+
for command in terraform_commands:
|
18
|
+
attempts = 3 if command == "terraform init" else 1 # terraform init can fail due to network issues
|
19
|
+
run_and_wait(command, cwd=tf_workdir, env=env_extra, attempts=attempts)
|
20
|
+
task.update(advance=1)
|
@@ -175,6 +175,14 @@ class TfExtSettings(StaticSettings):
|
|
175
175
|
def provider_cache_dir(self, provider_name: str) -> Path:
|
176
176
|
return self.cache_root / "provider_cache" / provider_name
|
177
177
|
|
178
|
+
@property
|
179
|
+
def variable_plan_resolvers_file_path(self) -> Path:
|
180
|
+
return self.static_root / "variable_plan_resolvers.yaml"
|
181
|
+
|
182
|
+
@property
|
183
|
+
def variable_plan_resolvers_dumped_file_path(self) -> Path:
|
184
|
+
return self.static_root / "variable_plan_resolvers_dumped.yaml"
|
185
|
+
|
178
186
|
|
179
187
|
def init_tf_ext_settings(*, allow_empty_out_path: bool = False) -> TfExtSettings:
|
180
188
|
settings = TfExtSettings.from_env()
|