qontract-reconcile 0.10.2.dev345__py3-none-any.whl → 0.10.2.dev408__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.
- {qontract_reconcile-0.10.2.dev345.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/METADATA +11 -10
- {qontract_reconcile-0.10.2.dev345.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/RECORD +126 -120
- reconcile/aus/base.py +17 -14
- reconcile/automated_actions/config/integration.py +12 -0
- reconcile/aws_account_manager/integration.py +2 -2
- reconcile/aws_ami_cleanup/integration.py +6 -7
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +2 -2
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +7 -1
- reconcile/aws_saml_roles/integration.py +9 -3
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +33 -8
- reconcile/dashdotdb_dora.py +4 -11
- reconcile/database_access_manager.py +118 -111
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request_manager.py +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +5 -3
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/model.py +10 -10
- reconcile/external_resources/reconciler.py +5 -2
- reconcile/external_resources/secrets_sync.py +4 -6
- reconcile/external_resources/state.py +5 -4
- reconcile/gabi_authorized_users.py +8 -5
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_mr_sqs_consumer.py +2 -2
- reconcile/gitlab_owners.py +15 -11
- reconcile/gql_definitions/automated_actions/instance.py +41 -2
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +10 -0
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +22 -61
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +10 -0
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +10 -0
- reconcile/gql_definitions/common/aws_vpc_requests.py +10 -0
- reconcile/gql_definitions/common/clusters.py +2 -0
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +84 -1
- reconcile/gql_definitions/external_resources/external_resources_settings.py +2 -0
- reconcile/gql_definitions/fragments/aws_account_common.py +2 -0
- reconcile/gql_definitions/fragments/aws_organization.py +33 -0
- reconcile/gql_definitions/fragments/aws_vpc_request.py +2 -0
- reconcile/gql_definitions/introspection.json +3474 -1986
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +4 -0
- reconcile/gql_definitions/terraform_init/aws_accounts.py +14 -0
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +33 -1
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +10 -0
- reconcile/jenkins_worker_fleets.py +1 -0
- reconcile/jira_permissions_validator.py +236 -121
- reconcile/ocm/types.py +6 -0
- reconcile/openshift_base.py +47 -1
- reconcile/openshift_cluster_bots.py +2 -1
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_saas_deploy.py +2 -2
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_upgrade_watcher.py +3 -3
- reconcile/queries.py +131 -0
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/slack_usergroups.py +4 -3
- reconcile/sql_query.py +1 -0
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/status.py +5 -8
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
- reconcile/templating/renderer.py +2 -1
- reconcile/terraform_aws_route53.py +7 -1
- reconcile/terraform_init/integration.py +185 -21
- reconcile/terraform_resources.py +11 -1
- reconcile/terraform_tgw_attachments.py +7 -1
- reconcile/terraform_users.py +7 -0
- reconcile/terraform_vpc_peerings.py +14 -3
- reconcile/terraform_vpc_resources/integration.py +7 -0
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/saas_files.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +3 -2
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/jinja2/utils.py +4 -96
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +9 -12
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +32 -0
- reconcile/utils/merge_request_manager/merge_request_manager.py +3 -3
- reconcile/utils/merge_request_manager/parser.py +2 -2
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/base.py +2 -2
- reconcile/utils/mr/notificator.py +2 -2
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/oc.py +113 -95
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/products.py +6 -0
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +3 -5
- reconcile/utils/openshift_resource.py +10 -5
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +5 -5
- reconcile/utils/runtime/integration.py +1 -2
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/models.py +2 -1
- reconcile/utils/saasherder/saasherder.py +9 -7
- reconcile/utils/slack_api.py +24 -2
- reconcile/utils/sloth.py +171 -2
- reconcile/utils/sqs_gateway.py +2 -1
- reconcile/utils/state.py +2 -1
- reconcile/utils/terraform_client.py +4 -3
- reconcile/utils/terrascript_aws_client.py +165 -111
- reconcile/utils/vault.py +1 -1
- reconcile/vault_replication.py +107 -42
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +25 -13
- {qontract_reconcile-0.10.2.dev345.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev345.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/entry_points.txt +0 -0
|
@@ -22,7 +22,6 @@ from typing import (
|
|
|
22
22
|
TYPE_CHECKING,
|
|
23
23
|
Any,
|
|
24
24
|
Self,
|
|
25
|
-
TypeAlias,
|
|
26
25
|
cast,
|
|
27
26
|
)
|
|
28
27
|
|
|
@@ -152,6 +151,7 @@ from reconcile.github_org import get_default_config
|
|
|
152
151
|
from reconcile.gql_definitions.terraform_resources.terraform_resources_namespaces import (
|
|
153
152
|
NamespaceTerraformResourceLifecycleV1,
|
|
154
153
|
)
|
|
154
|
+
from reconcile.typed_queries.aws_account_tags import get_aws_account_tags
|
|
155
155
|
from reconcile.utils import gql
|
|
156
156
|
from reconcile.utils.aws_api import (
|
|
157
157
|
AmiTag,
|
|
@@ -178,7 +178,11 @@ from reconcile.utils.external_resources import (
|
|
|
178
178
|
from reconcile.utils.git import is_file_in_git_repo
|
|
179
179
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
180
180
|
from reconcile.utils.jenkins_api import JenkinsApi
|
|
181
|
-
from reconcile.utils.jinja2.utils import
|
|
181
|
+
from reconcile.utils.jinja2.utils import (
|
|
182
|
+
process_extracurlyjinja2_template,
|
|
183
|
+
process_jinja2_template,
|
|
184
|
+
)
|
|
185
|
+
from reconcile.utils.json import json_dumps
|
|
182
186
|
from reconcile.utils.password_validator import (
|
|
183
187
|
PasswordPolicy,
|
|
184
188
|
PasswordValidator,
|
|
@@ -202,7 +206,7 @@ if TYPE_CHECKING:
|
|
|
202
206
|
from reconcile.utils.ocm import OCMMap
|
|
203
207
|
|
|
204
208
|
|
|
205
|
-
TFResource
|
|
209
|
+
type TFResource = type[
|
|
206
210
|
Resource | Data | Module | Provider | Variable | Output | Locals | Terraform
|
|
207
211
|
]
|
|
208
212
|
|
|
@@ -267,6 +271,7 @@ VARIABLE_KEYS = [
|
|
|
267
271
|
"extra_tags",
|
|
268
272
|
"lifecycle",
|
|
269
273
|
"max_session_duration",
|
|
274
|
+
"secret_format",
|
|
270
275
|
]
|
|
271
276
|
|
|
272
277
|
EMAIL_REGEX = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
|
@@ -286,12 +291,6 @@ SUPPORTED_ALB_LISTENER_RULE_CONDITION_TYPE_MAPPING = {
|
|
|
286
291
|
"source-ip": "source_ip",
|
|
287
292
|
}
|
|
288
293
|
|
|
289
|
-
DEFAULT_TAGS = {
|
|
290
|
-
"tags": {
|
|
291
|
-
"app": "app-sre-infra",
|
|
292
|
-
},
|
|
293
|
-
}
|
|
294
|
-
|
|
295
294
|
AWS_ELB_ACCOUNT_IDS = {
|
|
296
295
|
"us-east-1": "127311923021",
|
|
297
296
|
"us-east-2": "033677994240",
|
|
@@ -475,6 +474,7 @@ class TerrascriptClient:
|
|
|
475
474
|
integration_prefix: str,
|
|
476
475
|
thread_pool_size: int,
|
|
477
476
|
accounts: Iterable[MutableMapping[str, Any]],
|
|
477
|
+
default_tags: Mapping[str, str] | None,
|
|
478
478
|
settings: Mapping[str, Any] | None = None,
|
|
479
479
|
prefetch_resources_by_schemas: Iterable[str] | None = None,
|
|
480
480
|
secret_reader: SecretReaderBase | None = None,
|
|
@@ -488,6 +488,7 @@ class TerrascriptClient:
|
|
|
488
488
|
else:
|
|
489
489
|
self.secret_reader = SecretReader(settings=settings)
|
|
490
490
|
self.configs: dict[str, dict] = {}
|
|
491
|
+
self.default_tags = default_tags or {"app": "app-sre-infra"}
|
|
491
492
|
self.populate_configs(filtered_accounts)
|
|
492
493
|
self.versions: dict[str, str] = {
|
|
493
494
|
a["name"]: a["providerVersion"] for a in filtered_accounts
|
|
@@ -508,7 +509,7 @@ class TerrascriptClient:
|
|
|
508
509
|
region=region,
|
|
509
510
|
alias=region,
|
|
510
511
|
skip_region_validation=True,
|
|
511
|
-
default_tags=
|
|
512
|
+
default_tags={"tags": config["tags"]},
|
|
512
513
|
)
|
|
513
514
|
|
|
514
515
|
# Add default region, which will be in resourcesDefaultRegion
|
|
@@ -517,7 +518,7 @@ class TerrascriptClient:
|
|
|
517
518
|
secret_key=config["aws_secret_access_key"],
|
|
518
519
|
region=config["resourcesDefaultRegion"],
|
|
519
520
|
skip_region_validation=True,
|
|
520
|
-
default_tags=
|
|
521
|
+
default_tags={"tags": config["tags"]},
|
|
521
522
|
)
|
|
522
523
|
|
|
523
524
|
ts += Terraform(
|
|
@@ -800,6 +801,9 @@ class TerrascriptClient:
|
|
|
800
801
|
config["supportedDeploymentRegions"] = account["supportedDeploymentRegions"]
|
|
801
802
|
config["resourcesDefaultRegion"] = account["resourcesDefaultRegion"]
|
|
802
803
|
config["terraformState"] = account["terraformState"]
|
|
804
|
+
config["tags"] = dict(self.default_tags) | get_aws_account_tags(
|
|
805
|
+
account.get("organization", None)
|
|
806
|
+
)
|
|
803
807
|
self.configs[account_name] = config
|
|
804
808
|
|
|
805
809
|
def _get_partition(self, account: str) -> str:
|
|
@@ -1069,25 +1073,15 @@ class TerrascriptClient:
|
|
|
1069
1073
|
config = self.configs[account_name]
|
|
1070
1074
|
existing_provider_aliases = {p.get("alias") for p in ts["provider"]["aws"]}
|
|
1071
1075
|
if alias not in existing_provider_aliases:
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
)
|
|
1082
|
-
else:
|
|
1083
|
-
ts += provider.aws(
|
|
1084
|
-
access_key=config["aws_access_key_id"],
|
|
1085
|
-
secret_key=config["aws_secret_access_key"],
|
|
1086
|
-
region=region,
|
|
1087
|
-
alias=alias,
|
|
1088
|
-
skip_region_validation=True,
|
|
1089
|
-
default_tags=DEFAULT_TAGS,
|
|
1090
|
-
)
|
|
1076
|
+
ts += provider.aws(
|
|
1077
|
+
access_key=config["aws_access_key_id"],
|
|
1078
|
+
secret_key=config["aws_secret_access_key"],
|
|
1079
|
+
region=region,
|
|
1080
|
+
alias=alias,
|
|
1081
|
+
skip_region_validation=True,
|
|
1082
|
+
default_tags={"tags": config["tags"]},
|
|
1083
|
+
**{"assume_role": {"role_arn": assume_role}} if assume_role else {},
|
|
1084
|
+
)
|
|
1091
1085
|
|
|
1092
1086
|
def populate_route53(
|
|
1093
1087
|
self, desired_state: Iterable[Mapping[str, Any]], default_ttl: int = 300
|
|
@@ -1947,7 +1941,7 @@ class TerrascriptClient:
|
|
|
1947
1941
|
em_identifier = f"{identifier}-enhanced-monitoring"
|
|
1948
1942
|
em_values = {
|
|
1949
1943
|
"name": em_identifier,
|
|
1950
|
-
"assume_role_policy":
|
|
1944
|
+
"assume_role_policy": json_dumps(assume_role_policy),
|
|
1951
1945
|
}
|
|
1952
1946
|
role_tf_resource = aws_iam_role(em_identifier, **em_values)
|
|
1953
1947
|
tf_resources.append(role_tf_resource)
|
|
@@ -2216,6 +2210,43 @@ class TerrascriptClient:
|
|
|
2216
2210
|
letters_and_digits = string.ascii_letters + string.digits
|
|
2217
2211
|
return "".join(random.choice(letters_and_digits) for i in range(string_length))
|
|
2218
2212
|
|
|
2213
|
+
@staticmethod
|
|
2214
|
+
def _build_tf_resource_s3_lifecycle_rules(
|
|
2215
|
+
versioning: bool,
|
|
2216
|
+
common_values: Mapping[str, Any],
|
|
2217
|
+
) -> list[dict]:
|
|
2218
|
+
lifecycle_rules = common_values.get("lifecycle_rules") or []
|
|
2219
|
+
if versioning and not any(
|
|
2220
|
+
"noncurrent_version_expiration" in lr for lr in lifecycle_rules
|
|
2221
|
+
):
|
|
2222
|
+
# Add a default noncurrent object expiration rule
|
|
2223
|
+
# if one isn't already set
|
|
2224
|
+
rule = {
|
|
2225
|
+
"id": "expire_noncurrent_versions",
|
|
2226
|
+
"enabled": True,
|
|
2227
|
+
"noncurrent_version_expiration": {"days": 30},
|
|
2228
|
+
"expiration": {"expired_object_delete_marker": True},
|
|
2229
|
+
"abort_incomplete_multipart_upload_days": 3,
|
|
2230
|
+
}
|
|
2231
|
+
lifecycle_rules.append(rule)
|
|
2232
|
+
|
|
2233
|
+
if storage_class := common_values.get("storage_class"):
|
|
2234
|
+
sc = storage_class.upper()
|
|
2235
|
+
days = "1"
|
|
2236
|
+
if sc.endswith("_IA"):
|
|
2237
|
+
# Infrequent Access storage class has minimum 30 days
|
|
2238
|
+
# before transition
|
|
2239
|
+
days = "30"
|
|
2240
|
+
rule = {
|
|
2241
|
+
"id": sc + "_storage_class",
|
|
2242
|
+
"enabled": True,
|
|
2243
|
+
"transition": {"days": days, "storage_class": sc},
|
|
2244
|
+
"noncurrent_version_transition": {"days": days, "storage_class": sc},
|
|
2245
|
+
}
|
|
2246
|
+
lifecycle_rules.append(rule)
|
|
2247
|
+
|
|
2248
|
+
return lifecycle_rules
|
|
2249
|
+
|
|
2219
2250
|
def populate_tf_resource_s3(self, spec: ExternalResourceSpec) -> aws_s3_bucket:
|
|
2220
2251
|
account = spec.provisioner_name
|
|
2221
2252
|
identifier = spec.identifier
|
|
@@ -2255,47 +2286,11 @@ class TerrascriptClient:
|
|
|
2255
2286
|
request_payer = common_values.get("request_payer")
|
|
2256
2287
|
if request_payer:
|
|
2257
2288
|
values["request_payer"] = request_payer
|
|
2258
|
-
lifecycle_rules
|
|
2259
|
-
|
|
2260
|
-
|
|
2289
|
+
if lifecycle_rules := self._build_tf_resource_s3_lifecycle_rules(
|
|
2290
|
+
versioning=versioning,
|
|
2291
|
+
common_values=common_values,
|
|
2292
|
+
):
|
|
2261
2293
|
values["lifecycle_rule"] = lifecycle_rules
|
|
2262
|
-
if versioning:
|
|
2263
|
-
lrs = values.get("lifecycle_rule", [])
|
|
2264
|
-
expiration_rule = False
|
|
2265
|
-
for lr in lrs:
|
|
2266
|
-
if "noncurrent_version_expiration" in lr:
|
|
2267
|
-
expiration_rule = True
|
|
2268
|
-
break
|
|
2269
|
-
if not expiration_rule:
|
|
2270
|
-
# Add a default noncurrent object expiration rule if
|
|
2271
|
-
# if one isn't already set
|
|
2272
|
-
rule = {
|
|
2273
|
-
"id": "expire_noncurrent_versions",
|
|
2274
|
-
"enabled": "true",
|
|
2275
|
-
"noncurrent_version_expiration": {"days": 30},
|
|
2276
|
-
}
|
|
2277
|
-
if len(lrs) > 0:
|
|
2278
|
-
lrs.append(rule)
|
|
2279
|
-
else:
|
|
2280
|
-
lrs = rule
|
|
2281
|
-
sc = common_values.get("storage_class")
|
|
2282
|
-
if sc:
|
|
2283
|
-
sc = sc.upper()
|
|
2284
|
-
days = "1"
|
|
2285
|
-
if sc.endswith("_IA"):
|
|
2286
|
-
# Infrequent Access storage class has minimum 30 days
|
|
2287
|
-
# before transition
|
|
2288
|
-
days = "30"
|
|
2289
|
-
rule = {
|
|
2290
|
-
"id": sc + "_storage_class",
|
|
2291
|
-
"enabled": "true",
|
|
2292
|
-
"transition": {"days": days, "storage_class": sc},
|
|
2293
|
-
"noncurrent_version_transition": {"days": days, "storage_class": sc},
|
|
2294
|
-
}
|
|
2295
|
-
if values.get("lifecycle_rule"):
|
|
2296
|
-
values["lifecycle_rule"].append(rule)
|
|
2297
|
-
else:
|
|
2298
|
-
values["lifecycle_rule"] = rule
|
|
2299
2294
|
cors_rules = common_values.get("cors_rules")
|
|
2300
2295
|
if cors_rules:
|
|
2301
2296
|
# common_values['cors_rules'] is a list of cors_rules
|
|
@@ -2345,7 +2340,7 @@ class TerrascriptClient:
|
|
|
2345
2340
|
}
|
|
2346
2341
|
],
|
|
2347
2342
|
}
|
|
2348
|
-
rc_values["assume_role_policy"] =
|
|
2343
|
+
rc_values["assume_role_policy"] = json_dumps(role)
|
|
2349
2344
|
role_resource = aws_iam_role(id, **rc_values)
|
|
2350
2345
|
tf_resources.append(role_resource)
|
|
2351
2346
|
|
|
@@ -2383,7 +2378,7 @@ class TerrascriptClient:
|
|
|
2383
2378
|
},
|
|
2384
2379
|
],
|
|
2385
2380
|
}
|
|
2386
|
-
rc_values["policy"] =
|
|
2381
|
+
rc_values["policy"] = json_dumps(policy)
|
|
2387
2382
|
policy_resource = aws_iam_policy(id, **rc_values)
|
|
2388
2383
|
tf_resources.append(policy_resource)
|
|
2389
2384
|
|
|
@@ -2598,7 +2593,7 @@ class TerrascriptClient:
|
|
|
2598
2593
|
},
|
|
2599
2594
|
],
|
|
2600
2595
|
}
|
|
2601
|
-
values["policy"] =
|
|
2596
|
+
values["policy"] = json_dumps(policy)
|
|
2602
2597
|
values["depends_on"] = self.get_dependencies([user_tf_resource])
|
|
2603
2598
|
|
|
2604
2599
|
tf_aws_iam_policy = aws_iam_policy(identifier, **values)
|
|
@@ -2877,7 +2872,7 @@ class TerrascriptClient:
|
|
|
2877
2872
|
values: dict[str, Any] = {
|
|
2878
2873
|
"name": identifier,
|
|
2879
2874
|
"tags": common_values["tags"],
|
|
2880
|
-
"assume_role_policy":
|
|
2875
|
+
"assume_role_policy": json_dumps(assume_role_policy),
|
|
2881
2876
|
}
|
|
2882
2877
|
|
|
2883
2878
|
inline_policy = common_values.get("inline_policy")
|
|
@@ -2931,7 +2926,7 @@ class TerrascriptClient:
|
|
|
2931
2926
|
self, account: str, name: str, policy: Mapping[str, Any]
|
|
2932
2927
|
) -> None:
|
|
2933
2928
|
tf_aws_iam_policy = aws_iam_policy(
|
|
2934
|
-
f"{account}-{name}", name=name, policy=
|
|
2929
|
+
f"{account}-{name}", name=name, policy=json_dumps(policy)
|
|
2935
2930
|
)
|
|
2936
2931
|
self.add_resource(account, tf_aws_iam_policy)
|
|
2937
2932
|
|
|
@@ -2973,7 +2968,7 @@ class TerrascriptClient:
|
|
|
2973
2968
|
role_tf_resource = aws_iam_role(
|
|
2974
2969
|
f"{account}-{name}",
|
|
2975
2970
|
name=name,
|
|
2976
|
-
assume_role_policy=
|
|
2971
|
+
assume_role_policy=json_dumps(assume_role_policy),
|
|
2977
2972
|
managed_policy_arns=managed_policy_arns,
|
|
2978
2973
|
max_session_duration=max_session_duration_hours * 3600,
|
|
2979
2974
|
)
|
|
@@ -3017,7 +3012,7 @@ class TerrascriptClient:
|
|
|
3017
3012
|
all_queues.append(queue_name)
|
|
3018
3013
|
sqs_policy = values.pop("sqs_policy", None)
|
|
3019
3014
|
if sqs_policy is not None:
|
|
3020
|
-
values["policy"] =
|
|
3015
|
+
values["policy"] = json_dumps(sqs_policy)
|
|
3021
3016
|
dl_queue = values.pop("dl_queue", None)
|
|
3022
3017
|
if dl_queue is not None:
|
|
3023
3018
|
max_receive_count = int(values.pop("max_receive_count", 10))
|
|
@@ -3031,9 +3026,7 @@ class TerrascriptClient:
|
|
|
3031
3026
|
"deadLetterTargetArn": "${" + dl_data.arn + "}",
|
|
3032
3027
|
"maxReceiveCount": max_receive_count,
|
|
3033
3028
|
}
|
|
3034
|
-
values["redrive_policy"] =
|
|
3035
|
-
redrive_policy, sort_keys=True
|
|
3036
|
-
)
|
|
3029
|
+
values["redrive_policy"] = json_dumps(redrive_policy)
|
|
3037
3030
|
kms_master_key_id = values.pop("kms_master_key_id", None)
|
|
3038
3031
|
if kms_master_key_id is not None:
|
|
3039
3032
|
if kms_master_key_id.startswith("arn:"):
|
|
@@ -3106,7 +3099,7 @@ class TerrascriptClient:
|
|
|
3106
3099
|
"Resource": list(kms_keys),
|
|
3107
3100
|
}
|
|
3108
3101
|
policy["Statement"].append(kms_statement)
|
|
3109
|
-
values["policy"] =
|
|
3102
|
+
values["policy"] = json_dumps(policy)
|
|
3110
3103
|
policy_tf_resource = aws_iam_policy(policy_identifier, **values)
|
|
3111
3104
|
tf_resources.append(policy_tf_resource)
|
|
3112
3105
|
|
|
@@ -3256,7 +3249,7 @@ class TerrascriptClient:
|
|
|
3256
3249
|
}
|
|
3257
3250
|
],
|
|
3258
3251
|
}
|
|
3259
|
-
values["policy"] =
|
|
3252
|
+
values["policy"] = json_dumps(policy)
|
|
3260
3253
|
values["depends_on"] = self.get_dependencies([user_tf_resource])
|
|
3261
3254
|
|
|
3262
3255
|
tf_aws_iam_policy = aws_iam_policy(identifier, **values)
|
|
@@ -3366,7 +3359,7 @@ class TerrascriptClient:
|
|
|
3366
3359
|
},
|
|
3367
3360
|
],
|
|
3368
3361
|
}
|
|
3369
|
-
values_policy["policy"] =
|
|
3362
|
+
values_policy["policy"] = json_dumps(policy)
|
|
3370
3363
|
values_policy["depends_on"] = self.get_dependencies([user_tf_resource])
|
|
3371
3364
|
|
|
3372
3365
|
tf_aws_iam_policy = aws_iam_policy(identifier, **values_policy)
|
|
@@ -3416,7 +3409,7 @@ class TerrascriptClient:
|
|
|
3416
3409
|
}
|
|
3417
3410
|
],
|
|
3418
3411
|
}
|
|
3419
|
-
values_policy["policy"] =
|
|
3412
|
+
values_policy["policy"] = json_dumps(policy)
|
|
3420
3413
|
values_policy["depends_on"] = self.get_dependencies([bucket_tf_resource])
|
|
3421
3414
|
region = common_values.get("region") or self.default_regions.get(account)
|
|
3422
3415
|
assert region # make mypy happy
|
|
@@ -3562,7 +3555,7 @@ class TerrascriptClient:
|
|
|
3562
3555
|
}
|
|
3563
3556
|
],
|
|
3564
3557
|
}
|
|
3565
|
-
sqs_values["policy"] =
|
|
3558
|
+
sqs_values["policy"] = json_dumps(sqs_policy)
|
|
3566
3559
|
|
|
3567
3560
|
kms_encryption = common_values.get("kms_encryption", False)
|
|
3568
3561
|
if kms_encryption:
|
|
@@ -3598,7 +3591,7 @@ class TerrascriptClient:
|
|
|
3598
3591
|
},
|
|
3599
3592
|
],
|
|
3600
3593
|
}
|
|
3601
|
-
kms_values["policy"] =
|
|
3594
|
+
kms_values["policy"] = json_dumps(kms_policy)
|
|
3602
3595
|
if provider:
|
|
3603
3596
|
kms_values["provider"] = provider
|
|
3604
3597
|
|
|
@@ -3696,7 +3689,7 @@ class TerrascriptClient:
|
|
|
3696
3689
|
"Resource": [sqs_values["kms_master_key_id"]],
|
|
3697
3690
|
}
|
|
3698
3691
|
policy["Statement"].append(kms_statement)
|
|
3699
|
-
values_policy["policy"] =
|
|
3692
|
+
values_policy["policy"] = json_dumps(policy)
|
|
3700
3693
|
policy_tf_resource = aws_iam_policy(sqs_identifier, **values_policy)
|
|
3701
3694
|
tf_resources.append(policy_tf_resource)
|
|
3702
3695
|
|
|
@@ -3767,7 +3760,7 @@ class TerrascriptClient:
|
|
|
3767
3760
|
role_identifier = f"{identifier}-lambda-execution-role"
|
|
3768
3761
|
role_values = {
|
|
3769
3762
|
"name": role_identifier,
|
|
3770
|
-
"assume_role_policy":
|
|
3763
|
+
"assume_role_policy": json_dumps(assume_role_policy),
|
|
3771
3764
|
}
|
|
3772
3765
|
|
|
3773
3766
|
role_tf_resource = aws_iam_role(role_identifier, **role_values)
|
|
@@ -3799,7 +3792,7 @@ class TerrascriptClient:
|
|
|
3799
3792
|
|
|
3800
3793
|
policy_values = {
|
|
3801
3794
|
"role": "${" + role_tf_resource.id + "}",
|
|
3802
|
-
"policy":
|
|
3795
|
+
"policy": json_dumps(policy),
|
|
3803
3796
|
}
|
|
3804
3797
|
policy_tf_resource = aws_iam_role_policy(policy_identifier, **policy_values)
|
|
3805
3798
|
tf_resources.append(policy_tf_resource)
|
|
@@ -3927,7 +3920,7 @@ class TerrascriptClient:
|
|
|
3927
3920
|
}
|
|
3928
3921
|
values = {
|
|
3929
3922
|
"name": identifier,
|
|
3930
|
-
"policy":
|
|
3923
|
+
"policy": json_dumps(policy),
|
|
3931
3924
|
"depends_on": self.get_dependencies([user_tf_resource]),
|
|
3932
3925
|
}
|
|
3933
3926
|
|
|
@@ -4048,7 +4041,7 @@ class TerrascriptClient:
|
|
|
4048
4041
|
role_identifier = f"{identifier}-lambda-execution-role"
|
|
4049
4042
|
role_values = {
|
|
4050
4043
|
"name": role_identifier,
|
|
4051
|
-
"assume_role_policy":
|
|
4044
|
+
"assume_role_policy": json_dumps(assume_role_policy),
|
|
4052
4045
|
"tags": tags,
|
|
4053
4046
|
}
|
|
4054
4047
|
|
|
@@ -4085,7 +4078,7 @@ class TerrascriptClient:
|
|
|
4085
4078
|
policy_tf_resource = aws_iam_policy(
|
|
4086
4079
|
policy_identifier,
|
|
4087
4080
|
name=policy_identifier,
|
|
4088
|
-
policy=
|
|
4081
|
+
policy=json_dumps(policy),
|
|
4089
4082
|
tags=tags,
|
|
4090
4083
|
)
|
|
4091
4084
|
tf_resources.append(policy_tf_resource)
|
|
@@ -4300,7 +4293,7 @@ class TerrascriptClient:
|
|
|
4300
4293
|
# iam user policy
|
|
4301
4294
|
values_policy: dict[str, Any] = {
|
|
4302
4295
|
"name": identifier,
|
|
4303
|
-
"policy":
|
|
4296
|
+
"policy": json_dumps(policy),
|
|
4304
4297
|
"depends_on": self.get_dependencies([user_tf_resource]),
|
|
4305
4298
|
}
|
|
4306
4299
|
|
|
@@ -4418,10 +4411,7 @@ class TerrascriptClient:
|
|
|
4418
4411
|
|
|
4419
4412
|
:return: key is AWS account name and value is terraform configuration
|
|
4420
4413
|
"""
|
|
4421
|
-
return {
|
|
4422
|
-
name: json.dumps(ts, indent=2, sort_keys=True)
|
|
4423
|
-
for name, ts in self.tss.items()
|
|
4424
|
-
}
|
|
4414
|
+
return {name: json_dumps(ts, indent=2) for name, ts in self.tss.items()}
|
|
4425
4415
|
|
|
4426
4416
|
def init_values(
|
|
4427
4417
|
self, spec: ExternalResourceSpec, init_tags: bool = True
|
|
@@ -4533,7 +4523,7 @@ class TerrascriptClient:
|
|
|
4533
4523
|
output_name = output_format.format(
|
|
4534
4524
|
spec.output_prefix, self.integration_prefix, "annotations"
|
|
4535
4525
|
)
|
|
4536
|
-
anno_json =
|
|
4526
|
+
anno_json = json_dumps(spec.annotations()).encode("utf-8")
|
|
4537
4527
|
output_value = base64.b64encode(anno_json).decode()
|
|
4538
4528
|
tf_resources.append(Output(output_name, value=output_value))
|
|
4539
4529
|
|
|
@@ -4673,7 +4663,7 @@ class TerrascriptClient:
|
|
|
4673
4663
|
}
|
|
4674
4664
|
log_groups_policy_values = {
|
|
4675
4665
|
"policy_name": "es-log-publishing-permissions",
|
|
4676
|
-
"policy_document":
|
|
4666
|
+
"policy_document": json_dumps(log_groups_policy),
|
|
4677
4667
|
}
|
|
4678
4668
|
resource_policy = aws_cloudwatch_log_resource_policy(
|
|
4679
4669
|
"es_log_publishing_resource_policy",
|
|
@@ -5005,7 +4995,7 @@ class TerrascriptClient:
|
|
|
5005
4995
|
}
|
|
5006
4996
|
],
|
|
5007
4997
|
}
|
|
5008
|
-
es_values["access_policies"] =
|
|
4998
|
+
es_values["access_policies"] = json_dumps(access_policies)
|
|
5009
4999
|
|
|
5010
5000
|
region = values.get("region") or self.default_regions.get(account)
|
|
5011
5001
|
assert region # make mypy happy
|
|
@@ -5061,7 +5051,7 @@ class TerrascriptClient:
|
|
|
5061
5051
|
|
|
5062
5052
|
version_values = {
|
|
5063
5053
|
"secret_id": "${" + aws_secret_resource.id + "}",
|
|
5064
|
-
"secret_string":
|
|
5054
|
+
"secret_string": json_dumps(master_user),
|
|
5065
5055
|
}
|
|
5066
5056
|
if provider:
|
|
5067
5057
|
version_values["provider"] = provider
|
|
@@ -5088,7 +5078,7 @@ class TerrascriptClient:
|
|
|
5088
5078
|
iam_policy_resource = aws_iam_policy(
|
|
5089
5079
|
secret_identifier,
|
|
5090
5080
|
name=f"{identifier}-secretsmanager-policy",
|
|
5091
|
-
policy=
|
|
5081
|
+
policy=json_dumps(policy),
|
|
5092
5082
|
tags=tags,
|
|
5093
5083
|
)
|
|
5094
5084
|
tf_resources.append(iam_policy_resource)
|
|
@@ -5500,7 +5490,7 @@ class TerrascriptClient:
|
|
|
5500
5490
|
lb_access_logs_s3_bucket_policy_values = {
|
|
5501
5491
|
"provider": provider,
|
|
5502
5492
|
"bucket": f"${{{lb_access_logs_s3_bucket_tf_resource.id}}}",
|
|
5503
|
-
"policy":
|
|
5493
|
+
"policy": json_dumps(policy),
|
|
5504
5494
|
}
|
|
5505
5495
|
lb_access_logs_s3_bucket_policy_tf_resource = aws_s3_bucket_policy(
|
|
5506
5496
|
policy_identifier, **lb_access_logs_s3_bucket_policy_values
|
|
@@ -5813,9 +5803,13 @@ class TerrascriptClient:
|
|
|
5813
5803
|
assert secret # make mypy happy
|
|
5814
5804
|
secret_data = self.secret_reader.read_all(secret)
|
|
5815
5805
|
|
|
5806
|
+
secret_format = common_values.get("secret_format")
|
|
5807
|
+
if secret_format is not None:
|
|
5808
|
+
secret_data = self._apply_secret_format(str(secret_format), secret_data)
|
|
5809
|
+
|
|
5816
5810
|
version_values: dict[str, Any] = {
|
|
5817
5811
|
"secret_id": "${" + aws_secret_resource.id + "}",
|
|
5818
|
-
"secret_string":
|
|
5812
|
+
"secret_string": json_dumps(secret_data),
|
|
5819
5813
|
}
|
|
5820
5814
|
|
|
5821
5815
|
if self._multiregion_account(account):
|
|
@@ -5836,6 +5830,66 @@ class TerrascriptClient:
|
|
|
5836
5830
|
|
|
5837
5831
|
self.add_resources(account, tf_resources)
|
|
5838
5832
|
|
|
5833
|
+
@staticmethod
|
|
5834
|
+
def _unflatten_dotted_keys_dict(flat_dict: dict[str, str]) -> dict[str, Any]:
|
|
5835
|
+
"""Convert a flat dictionary with dotted keys to a nested dictionary.
|
|
5836
|
+
|
|
5837
|
+
Example:
|
|
5838
|
+
{"db.host": "localhost", "db.port": "5432"} ->
|
|
5839
|
+
{"db": {"host": "localhost", "port": "5432"}}
|
|
5840
|
+
|
|
5841
|
+
Raises:
|
|
5842
|
+
ValueError: If there are conflicting keys (e.g., "a.b" and "a.b.c")
|
|
5843
|
+
"""
|
|
5844
|
+
result: dict[str, Any] = {}
|
|
5845
|
+
for key, value in flat_dict.items():
|
|
5846
|
+
parts = key.split(".")
|
|
5847
|
+
current = result
|
|
5848
|
+
for i, part in enumerate(parts[:-1]):
|
|
5849
|
+
if part not in current:
|
|
5850
|
+
current[part] = {}
|
|
5851
|
+
elif not isinstance(current[part], dict):
|
|
5852
|
+
# Conflict: trying to traverse through a non-dict value
|
|
5853
|
+
conflicting_path = ".".join(parts[: i + 1])
|
|
5854
|
+
raise ValueError(
|
|
5855
|
+
f"Conflicting keys detected: '{conflicting_path}' is both a "
|
|
5856
|
+
f"value and a nested path in key '{key}'"
|
|
5857
|
+
)
|
|
5858
|
+
current = current[part]
|
|
5859
|
+
|
|
5860
|
+
# Check if we're trying to set a value where a dict already exists
|
|
5861
|
+
if parts[-1] in current and isinstance(current[parts[-1]], dict):
|
|
5862
|
+
raise ValueError(
|
|
5863
|
+
f"Conflicting keys detected: '{key}' conflicts with nested keys"
|
|
5864
|
+
)
|
|
5865
|
+
|
|
5866
|
+
current[parts[-1]] = value
|
|
5867
|
+
|
|
5868
|
+
return result
|
|
5869
|
+
|
|
5870
|
+
@staticmethod
|
|
5871
|
+
def _apply_secret_format(
|
|
5872
|
+
secret_format: str, secret_data: dict[str, str]
|
|
5873
|
+
) -> dict[str, str]:
|
|
5874
|
+
# Convert flat dict with dotted keys to nested dict for Jinja2
|
|
5875
|
+
nested_secret_data = TerrascriptClient._unflatten_dotted_keys_dict(secret_data)
|
|
5876
|
+
rendered_data = process_jinja2_template(secret_format, nested_secret_data)
|
|
5877
|
+
|
|
5878
|
+
parsed_data = json.loads(rendered_data)
|
|
5879
|
+
|
|
5880
|
+
if not isinstance(parsed_data, dict):
|
|
5881
|
+
raise ValueError("secret_format must be a dictionary")
|
|
5882
|
+
|
|
5883
|
+
# validate secret is a dict[str, str]
|
|
5884
|
+
for k, v in parsed_data.items():
|
|
5885
|
+
if not isinstance(k, str):
|
|
5886
|
+
raise ValueError(f"key '{k}' is not a string")
|
|
5887
|
+
|
|
5888
|
+
if not isinstance(v, str):
|
|
5889
|
+
raise ValueError(f"dictionary value '{v}' under '{k}' is not a string")
|
|
5890
|
+
|
|
5891
|
+
return parsed_data
|
|
5892
|
+
|
|
5839
5893
|
def get_commit_sha(self, repo_info: Mapping) -> str:
|
|
5840
5894
|
url = repo_info["url"]
|
|
5841
5895
|
ref = repo_info["ref"]
|
|
@@ -6135,7 +6189,7 @@ class TerrascriptClient:
|
|
|
6135
6189
|
lambda_iam_role_resource = aws_iam_role(
|
|
6136
6190
|
"lambda_role",
|
|
6137
6191
|
name=f"ocm-{identifier}-cognito-lambda-role",
|
|
6138
|
-
assume_role_policy=
|
|
6192
|
+
assume_role_policy=json_dumps(lambda_role_policy),
|
|
6139
6193
|
managed_policy_arns=[lambda_managed_policy_arn],
|
|
6140
6194
|
force_detach_policies=False,
|
|
6141
6195
|
max_session_duration=3600,
|
|
@@ -6810,7 +6864,7 @@ class TerrascriptClient:
|
|
|
6810
6864
|
)
|
|
6811
6865
|
tf_resources.append(api_gateway_stage_resource)
|
|
6812
6866
|
|
|
6813
|
-
rest_api_policy =
|
|
6867
|
+
rest_api_policy = json_dumps({
|
|
6814
6868
|
"Version": "2012-10-17",
|
|
6815
6869
|
"Statement": [
|
|
6816
6870
|
{
|
|
@@ -6914,7 +6968,7 @@ class TerrascriptClient:
|
|
|
6914
6968
|
},
|
|
6915
6969
|
],
|
|
6916
6970
|
}
|
|
6917
|
-
cloudwatch_assume_role_policy =
|
|
6971
|
+
cloudwatch_assume_role_policy = json_dumps(policy)
|
|
6918
6972
|
|
|
6919
6973
|
cloudwatch_iam_role_resource = aws_iam_role(
|
|
6920
6974
|
"cloudwatch_assume_role",
|
|
@@ -6942,7 +6996,7 @@ class TerrascriptClient:
|
|
|
6942
6996
|
],
|
|
6943
6997
|
}
|
|
6944
6998
|
|
|
6945
|
-
cloudwatch_iam_policy_document =
|
|
6999
|
+
cloudwatch_iam_policy_document = json_dumps(policy)
|
|
6946
7000
|
|
|
6947
7001
|
cloudwatch_iam_policy_resource = aws_iam_policy(
|
|
6948
7002
|
"cloudwatch",
|
|
@@ -7187,7 +7241,7 @@ class TerrascriptClient:
|
|
|
7187
7241
|
|
|
7188
7242
|
version_values = {
|
|
7189
7243
|
"secret_id": "${" + secret_resource.arn + "}",
|
|
7190
|
-
"secret_string":
|
|
7244
|
+
"secret_string": json_dumps(secret),
|
|
7191
7245
|
}
|
|
7192
7246
|
version_resource = aws_secretsmanager_secret_version(
|
|
7193
7247
|
secret_identifier, **version_values
|
|
@@ -7196,7 +7250,7 @@ class TerrascriptClient:
|
|
|
7196
7250
|
|
|
7197
7251
|
secret_policy_values = {
|
|
7198
7252
|
"secret_arn": "${" + secret_resource.arn + "}",
|
|
7199
|
-
"policy":
|
|
7253
|
+
"policy": json_dumps({
|
|
7200
7254
|
"Version": "2012-10-17",
|
|
7201
7255
|
"Statement": [
|
|
7202
7256
|
{
|
reconcile/utils/vault.py
CHANGED
|
@@ -207,7 +207,7 @@ class VaultClient:
|
|
|
207
207
|
a v2 KV engine)
|
|
208
208
|
"""
|
|
209
209
|
secret_path = secret["path"]
|
|
210
|
-
secret_version = secret.get("version")
|
|
210
|
+
secret_version = secret.get("version", SECRET_VERSION_LATEST)
|
|
211
211
|
|
|
212
212
|
kv_version = self._get_mount_version_by_secret_path(secret_path)
|
|
213
213
|
|