qontract-reconcile 0.10.2.dev299__py3-none-any.whl → 0.10.2.dev430__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +399 -394
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +18 -12
  5. reconcile/aus/base.py +134 -32
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +3 -1
  8. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  9. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  10. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  11. reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
  12. reconcile/automated_actions/config/integration.py +16 -4
  13. reconcile/aws_account_manager/integration.py +8 -8
  14. reconcile/aws_account_manager/reconciler.py +3 -3
  15. reconcile/aws_ami_cleanup/integration.py +8 -12
  16. reconcile/aws_ami_share.py +69 -62
  17. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  18. reconcile/aws_ecr_image_pull_secrets.py +4 -4
  19. reconcile/aws_iam_keys.py +1 -0
  20. reconcile/aws_saml_idp/integration.py +12 -4
  21. reconcile/aws_saml_roles/integration.py +32 -25
  22. reconcile/aws_version_sync/integration.py +125 -84
  23. reconcile/change_owners/bundle.py +3 -3
  24. reconcile/change_owners/change_log_tracking.py +3 -2
  25. reconcile/change_owners/change_owners.py +1 -1
  26. reconcile/change_owners/diff.py +2 -4
  27. reconcile/checkpoint.py +12 -4
  28. reconcile/cli.py +111 -18
  29. reconcile/cluster_deployment_mapper.py +2 -3
  30. reconcile/dashdotdb_dora.py +5 -12
  31. reconcile/dashdotdb_slo.py +1 -1
  32. reconcile/database_access_manager.py +125 -121
  33. reconcile/deadmanssnitch.py +1 -5
  34. reconcile/dynatrace_token_provider/integration.py +1 -1
  35. reconcile/endpoints_discovery/integration.py +4 -1
  36. reconcile/endpoints_discovery/merge_request.py +1 -1
  37. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  38. reconcile/external_resources/factories.py +5 -12
  39. reconcile/external_resources/integration.py +1 -1
  40. reconcile/external_resources/manager.py +8 -5
  41. reconcile/external_resources/meta.py +0 -1
  42. reconcile/external_resources/metrics.py +1 -1
  43. reconcile/external_resources/model.py +20 -20
  44. reconcile/external_resources/reconciler.py +7 -4
  45. reconcile/external_resources/secrets_sync.py +8 -11
  46. reconcile/external_resources/state.py +26 -16
  47. reconcile/fleet_labeler/integration.py +1 -1
  48. reconcile/gabi_authorized_users.py +8 -5
  49. reconcile/gcp_image_mirror.py +2 -2
  50. reconcile/github_org.py +1 -1
  51. reconcile/github_owners.py +4 -0
  52. reconcile/gitlab_housekeeping.py +13 -15
  53. reconcile/gitlab_members.py +6 -12
  54. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  55. reconcile/gitlab_owners.py +15 -11
  56. reconcile/gitlab_permissions.py +8 -12
  57. reconcile/glitchtip_project_alerts/integration.py +3 -1
  58. reconcile/gql_definitions/acs/acs_instances.py +10 -10
  59. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  60. reconcile/gql_definitions/acs/acs_rbac.py +6 -6
  61. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
  62. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
  63. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
  64. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  65. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  66. reconcile/gql_definitions/automated_actions/instance.py +51 -12
  67. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
  68. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
  69. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
  70. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
  71. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
  72. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  73. reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
  74. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  75. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  76. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
  77. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
  78. reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
  79. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  80. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  81. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_roles.py +120 -0
  84. reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
  85. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  87. reconcile/gql_definitions/common/apps.py +5 -5
  88. reconcile/gql_definitions/common/aws_vpc_requests.py +23 -10
  89. reconcile/gql_definitions/common/aws_vpcs.py +11 -11
  90. reconcile/gql_definitions/common/clusters.py +37 -35
  91. reconcile/gql_definitions/common/clusters_minimal.py +14 -14
  92. reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
  93. reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
  94. reconcile/gql_definitions/common/github_orgs.py +10 -10
  95. reconcile/gql_definitions/common/jira_settings.py +10 -10
  96. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  97. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  98. reconcile/gql_definitions/common/namespaces.py +42 -44
  99. reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
  100. reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
  101. reconcile/gql_definitions/common/ocm_environments.py +19 -19
  102. reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
  103. reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
  104. reconcile/gql_definitions/common/pipeline_providers.py +29 -29
  105. reconcile/gql_definitions/common/quay_instances.py +5 -5
  106. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  107. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  108. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  109. reconcile/gql_definitions/common/saas_files.py +44 -44
  110. reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
  111. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  112. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  113. reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
  114. reconcile/gql_definitions/common/state_aws_account.py +7 -8
  115. reconcile/gql_definitions/common/users.py +5 -5
  116. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  117. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  118. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  119. reconcile/gql_definitions/cost_report/settings.py +9 -9
  120. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
  121. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
  122. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  123. reconcile/gql_definitions/email_sender/apps.py +5 -5
  124. reconcile/gql_definitions/email_sender/emails.py +8 -8
  125. reconcile/gql_definitions/email_sender/users.py +6 -6
  126. reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
  127. reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
  128. reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
  129. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +492 -410
  130. reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
  131. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  132. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
  133. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  135. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  136. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  138. reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
  139. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_vpc_request.py +10 -5
  141. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  142. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  143. reconcile/gql_definitions/fragments/disable.py +5 -5
  144. reconcile/gql_definitions/fragments/email_service.py +5 -5
  145. reconcile/gql_definitions/fragments/email_user.py +5 -5
  146. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  147. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  148. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  149. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  150. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  151. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  152. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  153. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  154. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  155. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  156. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  157. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  158. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  159. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  160. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  161. reconcile/gql_definitions/fragments/user.py +5 -5
  162. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  163. reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
  164. reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
  165. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
  166. reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
  167. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
  168. reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
  169. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
  170. reconcile/gql_definitions/integrations/integrations.py +48 -51
  171. reconcile/gql_definitions/introspection.json +3050 -1393
  172. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
  173. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
  174. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  175. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
  176. reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
  177. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  178. reconcile/gql_definitions/ldap_groups/settings.py +9 -9
  179. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  180. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  181. reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
  182. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  183. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
  184. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  185. reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
  186. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
  187. reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
  188. reconcile/gql_definitions/rhcs/certs.py +33 -87
  189. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  190. reconcile/gql_definitions/rhidp/organizations.py +18 -18
  191. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  192. reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
  193. reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
  194. reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
  195. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  196. reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
  197. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  198. reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
  199. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  200. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  201. reconcile/gql_definitions/status_board/status_board.py +6 -7
  202. reconcile/gql_definitions/statuspage/statuspages.py +9 -9
  203. reconcile/gql_definitions/templating/template_collection.py +5 -5
  204. reconcile/gql_definitions/templating/templates.py +5 -5
  205. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
  206. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
  207. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
  208. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
  209. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
  210. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
  211. reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
  212. reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
  213. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  214. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +448 -402
  215. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
  216. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
  217. reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
  218. reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
  219. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
  220. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  221. reconcile/integrations_manager.py +3 -3
  222. reconcile/jenkins_job_builder.py +1 -1
  223. reconcile/jenkins_worker_fleets.py +80 -11
  224. reconcile/jira_permissions_validator.py +237 -122
  225. reconcile/ldap_groups/integration.py +1 -1
  226. reconcile/ocm/types.py +35 -56
  227. reconcile/ocm_aws_infrastructure_access.py +1 -1
  228. reconcile/ocm_clusters.py +4 -4
  229. reconcile/ocm_labels/integration.py +3 -2
  230. reconcile/ocm_machine_pools.py +33 -27
  231. reconcile/openshift_base.py +122 -10
  232. reconcile/openshift_cluster_bots.py +5 -5
  233. reconcile/openshift_groups.py +5 -0
  234. reconcile/openshift_limitranges.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +97 -101
  237. reconcile/openshift_resources_base.py +10 -5
  238. reconcile/openshift_rhcs_certs.py +77 -40
  239. reconcile/openshift_rolebindings.py +230 -130
  240. reconcile/openshift_saas_deploy.py +6 -7
  241. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  242. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  243. reconcile/openshift_serviceaccount_tokens.py +8 -7
  244. reconcile/openshift_tekton_resources.py +1 -1
  245. reconcile/openshift_upgrade_watcher.py +4 -4
  246. reconcile/openshift_users.py +5 -3
  247. reconcile/oum/labelset.py +5 -3
  248. reconcile/oum/models.py +1 -4
  249. reconcile/oum/providers.py +1 -1
  250. reconcile/prometheus_rules_tester/integration.py +4 -4
  251. reconcile/quay_mirror.py +1 -1
  252. reconcile/queries.py +131 -0
  253. reconcile/requests_sender.py +8 -3
  254. reconcile/resource_scraper.py +1 -5
  255. reconcile/rhidp/common.py +5 -5
  256. reconcile/rhidp/sso_client/base.py +19 -10
  257. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  258. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  259. reconcile/sendgrid_teammates.py +20 -9
  260. reconcile/skupper_network/integration.py +2 -2
  261. reconcile/slack_usergroups.py +35 -14
  262. reconcile/sql_query.py +1 -0
  263. reconcile/status.py +2 -2
  264. reconcile/status_board.py +6 -6
  265. reconcile/statuspage/atlassian.py +7 -7
  266. reconcile/statuspage/integrations/maintenances.py +4 -3
  267. reconcile/statuspage/page.py +4 -9
  268. reconcile/statuspage/status.py +5 -8
  269. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
  270. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  271. reconcile/templating/lib/merge_request_manager.py +2 -2
  272. reconcile/templating/lib/rendering.py +3 -3
  273. reconcile/templating/renderer.py +12 -13
  274. reconcile/terraform_aws_route53.py +18 -8
  275. reconcile/terraform_cloudflare_dns.py +3 -3
  276. reconcile/terraform_cloudflare_resources.py +12 -13
  277. reconcile/terraform_cloudflare_users.py +3 -2
  278. reconcile/terraform_init/integration.py +187 -23
  279. reconcile/terraform_repo.py +16 -12
  280. reconcile/terraform_resources.py +18 -10
  281. reconcile/terraform_tgw_attachments.py +27 -19
  282. reconcile/terraform_users.py +29 -21
  283. reconcile/terraform_vpc_peerings.py +16 -4
  284. reconcile/terraform_vpc_resources/integration.py +32 -2
  285. reconcile/typed_queries/app_interface_roles.py +10 -0
  286. reconcile/typed_queries/aws_account_tags.py +41 -0
  287. reconcile/typed_queries/cost_report/app_names.py +1 -1
  288. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  289. reconcile/typed_queries/saas_files.py +13 -13
  290. reconcile/typed_queries/status_board.py +2 -2
  291. reconcile/unleash_feature_toggles/integration.py +4 -2
  292. reconcile/utils/acs/base.py +6 -3
  293. reconcile/utils/acs/policies.py +2 -2
  294. reconcile/utils/aggregated_list.py +4 -3
  295. reconcile/utils/aws_api.py +51 -20
  296. reconcile/utils/aws_api_typed/api.py +38 -9
  297. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  298. reconcile/utils/aws_api_typed/logs.py +73 -0
  299. reconcile/utils/aws_api_typed/organization.py +4 -2
  300. reconcile/utils/binary.py +7 -12
  301. reconcile/utils/datetime_util.py +67 -0
  302. reconcile/utils/deadmanssnitch_api.py +1 -1
  303. reconcile/utils/differ.py +2 -3
  304. reconcile/utils/early_exit_cache.py +11 -12
  305. reconcile/utils/expiration.py +7 -3
  306. reconcile/utils/external_resource_spec.py +24 -1
  307. reconcile/utils/filtering.py +1 -1
  308. reconcile/utils/gitlab_api.py +7 -5
  309. reconcile/utils/glitchtip/client.py +6 -2
  310. reconcile/utils/glitchtip/models.py +25 -28
  311. reconcile/utils/gpg.py +5 -3
  312. reconcile/utils/gql.py +4 -7
  313. reconcile/utils/helm.py +2 -1
  314. reconcile/utils/helpers.py +1 -1
  315. reconcile/utils/imap_client.py +1 -1
  316. reconcile/utils/instrumented_wrappers.py +1 -1
  317. reconcile/utils/internal_groups/client.py +2 -2
  318. reconcile/utils/internal_groups/models.py +8 -17
  319. reconcile/utils/jenkins_api.py +24 -1
  320. reconcile/utils/jinja2/utils.py +6 -8
  321. reconcile/utils/jira_client.py +82 -63
  322. reconcile/utils/jjb_client.py +59 -43
  323. reconcile/utils/jobcontroller/controller.py +2 -2
  324. reconcile/utils/jobcontroller/models.py +17 -1
  325. reconcile/utils/json.py +74 -0
  326. reconcile/utils/ldap_client.py +4 -3
  327. reconcile/utils/lean_terraform_client.py +3 -1
  328. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  329. reconcile/utils/membershipsources/models.py +16 -23
  330. reconcile/utils/membershipsources/resolver.py +4 -2
  331. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  332. reconcile/utils/merge_request_manager/parser.py +6 -6
  333. reconcile/utils/metrics.py +5 -5
  334. reconcile/utils/models.py +304 -82
  335. reconcile/utils/mr/__init__.py +3 -1
  336. reconcile/utils/mr/app_interface_reporter.py +6 -3
  337. reconcile/utils/mr/aws_access.py +1 -1
  338. reconcile/utils/mr/base.py +7 -13
  339. reconcile/utils/mr/clusters_updates.py +4 -2
  340. reconcile/utils/mr/notificator.py +3 -3
  341. reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
  342. reconcile/utils/mr/promote_qontract.py +28 -12
  343. reconcile/utils/mr/update_access_report_base.py +3 -4
  344. reconcile/utils/mr/user_maintenance.py +7 -6
  345. reconcile/utils/oc.py +445 -336
  346. reconcile/utils/oc_filters.py +3 -3
  347. reconcile/utils/ocm/addons.py +0 -1
  348. reconcile/utils/ocm/base.py +27 -20
  349. reconcile/utils/ocm/cluster_groups.py +1 -1
  350. reconcile/utils/ocm/identity_providers.py +2 -2
  351. reconcile/utils/ocm/labels.py +1 -1
  352. reconcile/utils/ocm/ocm.py +81 -71
  353. reconcile/utils/ocm/products.py +9 -3
  354. reconcile/utils/ocm/search_filters.py +3 -6
  355. reconcile/utils/ocm/service_log.py +4 -6
  356. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  357. reconcile/utils/ocm_base_client.py +4 -4
  358. reconcile/utils/openshift_resource.py +83 -52
  359. reconcile/utils/openssl.py +2 -2
  360. reconcile/utils/output.py +3 -2
  361. reconcile/utils/pagerduty_api.py +10 -7
  362. reconcile/utils/promotion_state.py +6 -11
  363. reconcile/utils/raw_github_api.py +11 -8
  364. reconcile/utils/repo_owners.py +21 -29
  365. reconcile/utils/rhcsv2_certs.py +138 -35
  366. reconcile/utils/rosa/session.py +16 -0
  367. reconcile/utils/runtime/integration.py +2 -3
  368. reconcile/utils/runtime/meta.py +2 -1
  369. reconcile/utils/runtime/runner.py +2 -2
  370. reconcile/utils/saasherder/interfaces.py +13 -20
  371. reconcile/utils/saasherder/models.py +25 -21
  372. reconcile/utils/saasherder/saasherder.py +60 -32
  373. reconcile/utils/secret_reader.py +6 -6
  374. reconcile/utils/sharding.py +1 -1
  375. reconcile/utils/slack_api.py +26 -4
  376. reconcile/utils/sloth.py +224 -0
  377. reconcile/utils/sqs_gateway.py +16 -11
  378. reconcile/utils/state.py +2 -1
  379. reconcile/utils/structs.py +4 -4
  380. reconcile/utils/terraform_client.py +32 -29
  381. reconcile/utils/terrascript_aws_client.py +658 -480
  382. reconcile/utils/three_way_diff_strategy.py +1 -1
  383. reconcile/utils/throughput.py +1 -1
  384. reconcile/utils/unleash/server.py +2 -8
  385. reconcile/utils/vault.py +44 -41
  386. reconcile/utils/vcs.py +8 -8
  387. reconcile/vault_replication.py +119 -58
  388. reconcile/vpc_peerings_validator.py +2 -2
  389. tools/app_interface_reporter.py +4 -4
  390. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  391. tools/cli_commands/cost_report/view.py +7 -6
  392. tools/cli_commands/erv2.py +1 -1
  393. tools/cli_commands/gpg_encrypt.py +4 -1
  394. tools/cli_commands/systems_and_tools.py +5 -1
  395. tools/qontract_cli.py +36 -21
  396. tools/sre_checkpoints/util.py +5 -3
  397. tools/template_validation.py +3 -1
  398. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  399. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  400. reconcile/jenkins/__init__.py +0 -0
  401. reconcile/jenkins/types.py +0 -77
  402. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
  403. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  import sys
2
+ from collections.abc import Iterable, Mapping
2
3
  from textwrap import indent
3
4
  from typing import (
4
5
  Any,
5
- cast,
6
6
  )
7
7
 
8
8
  from reconcile import (
@@ -11,6 +11,7 @@ from reconcile import (
11
11
  )
12
12
  from reconcile.change_owners.diff import IDENTIFIER_FIELD_NAME
13
13
  from reconcile.gql_definitions.common.pgp_reencryption_settings import query
14
+ from reconcile.typed_queries.external_resources import get_settings
14
15
  from reconcile.utils import (
15
16
  expiration,
16
17
  gql,
@@ -30,7 +31,6 @@ from reconcile.utils.terraform_client import TerraformClient as Terraform
30
31
  from reconcile.utils.terrascript_aws_client import TerrascriptClient as Terrascript
31
32
  from reconcile.utils.vault import (
32
33
  VaultClient,
33
- _VaultClient,
34
34
  )
35
35
 
36
36
  TF_POLICY = """
@@ -81,8 +81,10 @@ QONTRACT_INTEGRATION = "terraform_users"
81
81
  QONTRACT_INTEGRATION_VERSION = make_semver(0, 4, 2)
82
82
  QONTRACT_TF_PREFIX = "qrtf"
83
83
 
84
+ Role = dict[str, Any]
84
85
 
85
- def get_tf_roles() -> list[dict[str, Any]]:
86
+
87
+ def get_tf_roles() -> list[Role]:
86
88
  gqlapi = gql.get_api()
87
89
  roles: list[dict] = expiration.filter(gqlapi.query(TF_QUERY)["roles"])
88
90
  return [
@@ -93,9 +95,9 @@ def get_tf_roles() -> list[dict[str, Any]]:
93
95
 
94
96
 
95
97
  def _filter_participating_aws_accounts(
96
- accounts: list,
97
- roles: list[dict[str, Any]],
98
- ) -> list:
98
+ accounts: Iterable[dict[str, Any]],
99
+ roles: Iterable[Mapping[str, Any]],
100
+ ) -> list[dict[str, Any]]:
99
101
  participating_aws_account_names: set[str] = set()
100
102
  for role in roles:
101
103
  participating_aws_account_names.update(
@@ -109,7 +111,7 @@ def _filter_participating_aws_accounts(
109
111
 
110
112
 
111
113
  def setup(
112
- print_to_file,
114
+ print_to_file: str | None,
113
115
  thread_pool_size: int,
114
116
  skip_reencrypt_accounts: list[str],
115
117
  appsre_pgp_key: str | None = None,
@@ -125,12 +127,18 @@ def setup(
125
127
  participating_aws_accounts = _filter_participating_aws_accounts(accounts, roles)
126
128
 
127
129
  settings = queries.get_app_interface_settings()
130
+ try:
131
+ default_tags = get_settings().default_tags
132
+ except ValueError:
133
+ # no external resources settings found
134
+ default_tags = None
128
135
  ts = Terrascript(
129
136
  QONTRACT_INTEGRATION,
130
137
  QONTRACT_TF_PREFIX,
131
138
  thread_pool_size,
132
139
  participating_aws_accounts,
133
140
  settings=settings,
141
+ default_tags=default_tags,
134
142
  )
135
143
  err = ts.populate_users(
136
144
  roles,
@@ -144,10 +152,10 @@ def setup(
144
152
 
145
153
 
146
154
  def send_email_invites(
147
- new_users,
155
+ new_users: Iterable[tuple[str, str, str, str]],
148
156
  smtp_client: SmtpClient,
149
- skip_reencrypt_accounts: list[str],
150
- ):
157
+ skip_reencrypt_accounts: Iterable[str],
158
+ ) -> None:
151
159
  msg_template = """
152
160
  You have been invited to join the {} AWS account!
153
161
  Below you will find credentials for the first sign in.
@@ -185,11 +193,11 @@ Encrypted password: {}
185
193
 
186
194
 
187
195
  def write_user_to_vault(
188
- vault_client: _VaultClient,
196
+ vault_client: VaultClient,
189
197
  vault_path: str,
190
- new_users: list[tuple[str, str, str, str]],
191
- skip_reencrypt_accounts: list[str],
192
- ):
198
+ new_users: Iterable[tuple[str, str, str, str]],
199
+ skip_reencrypt_accounts: Iterable[str],
200
+ ) -> None:
193
201
  for account, console_url, user_name, enc_password in new_users:
194
202
  if account in skip_reencrypt_accounts:
195
203
  continue
@@ -206,13 +214,13 @@ def write_user_to_vault(
206
214
  vault_client.write(desired_secret, decode_base64=False)
207
215
 
208
216
 
209
- def cleanup_and_exit(tf=None, status=False):
217
+ def cleanup_and_exit(tf: Terraform | None = None, status: bool = False) -> None:
210
218
  if tf is not None:
211
219
  tf.cleanup()
212
220
  sys.exit(status)
213
221
 
214
222
 
215
- def get_reencrypt_settings():
223
+ def get_reencrypt_settings() -> tuple[list[str], str | None, Any]:
216
224
  all_reencrypt_settings = query(
217
225
  query_func=gql.get_api().query
218
226
  ).pgp_reencryption_settings
@@ -241,7 +249,7 @@ def run(
241
249
  thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
242
250
  send_mails: bool = True,
243
251
  account_name: str | None = None,
244
- ):
252
+ ) -> None:
245
253
  skip_accounts, appsre_pgp_key, reencrypt_settings = get_reencrypt_settings()
246
254
 
247
255
  # setup errors should skip resources that will lead
@@ -295,7 +303,7 @@ def run(
295
303
  new_users = tf.get_new_users()
296
304
 
297
305
  if reencrypt_settings:
298
- vc = cast("_VaultClient", VaultClient())
306
+ vc = VaultClient.get_instance()
299
307
  write_user_to_vault(
300
308
  vc, reencrypt_settings.reencrypt_vault_path, new_users, skip_accounts
301
309
  )
@@ -317,7 +325,7 @@ def run(
317
325
  cleanup_and_exit(tf, setup_err)
318
326
 
319
327
 
320
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
328
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
321
329
  """
322
330
  Finding diffs in deeply nested structures is time/resource consuming.
323
331
  Having a unique known property to identify objects makes it easier to match
@@ -328,11 +336,11 @@ def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
328
336
  for the DeepDiff library used in qontract-reconcile.
329
337
  """
330
338
 
331
- def add_account_identity(acc):
339
+ def add_account_identity(acc: dict[str, Any]) -> dict[str, Any]:
332
340
  acc[IDENTIFIER_FIELD_NAME] = acc["path"]
333
341
  return acc
334
342
 
335
- def add_role_identity(role):
343
+ def add_role_identity(role: dict[str, Any]) -> dict[str, Any]:
336
344
  role[IDENTIFIER_FIELD_NAME] = role["name"]
337
345
  return role
338
346
 
@@ -7,6 +7,7 @@ from typing import Any, TypedDict
7
7
  import reconcile.utils.terraform_client as terraform
8
8
  import reconcile.utils.terrascript_aws_client as terrascript
9
9
  from reconcile import queries
10
+ from reconcile.typed_queries import external_resources
10
11
  from reconcile.utils import (
11
12
  aws_api,
12
13
  ocm,
@@ -73,6 +74,7 @@ def _build_infrastructure_assume_role(
73
74
  ocm: OCM | None,
74
75
  provided_assume_role: str | None,
75
76
  ) -> dict[str, Any] | None:
77
+ assume_role: str | None = None
76
78
  if provided_assume_role:
77
79
  assume_role = provided_assume_role
78
80
  elif cluster["spec"].get("account"):
@@ -565,7 +567,7 @@ def build_desired_state_vpc(
565
567
  @defer
566
568
  def run(
567
569
  dry_run: bool,
568
- print_to_file: bool | None = None,
570
+ print_to_file: str | None = None,
569
571
  enable_deletion: bool = False,
570
572
  thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
571
573
  account_name: str | None = None,
@@ -653,8 +655,18 @@ def run(
653
655
  ])
654
656
 
655
657
  account_by_name = {a["name"]: a for a in accounts}
658
+ try:
659
+ default_tags = external_resources.get_settings().default_tags
660
+ except ValueError:
661
+ # no external resources settings found
662
+ default_tags = None
656
663
  with terrascript.TerrascriptClient(
657
- QONTRACT_INTEGRATION, "", thread_pool_size, infra_accounts, settings=settings
664
+ QONTRACT_INTEGRATION,
665
+ "",
666
+ thread_pool_size,
667
+ infra_accounts,
668
+ settings=settings,
669
+ default_tags=default_tags,
658
670
  ) as ts:
659
671
  rosa_cluster_accounts = [
660
672
  account_by_name[c["spec"]["account"]["name"]]
@@ -663,8 +675,8 @@ def run(
663
675
  ]
664
676
  ts.populate_configs(rosa_cluster_accounts)
665
677
 
666
- for infra_account_name, items in participating_accounts.items():
667
- ts.populate_additional_providers(infra_account_name, items)
678
+ for infra_account_name, accounts in participating_accounts.items():
679
+ ts.populate_additional_providers(infra_account_name, accounts)
668
680
  ts.populate_vpc_peerings(desired_state)
669
681
  working_dirs = ts.dump(print_to_file=print_to_file)
670
682
  terraform_configurations = ts.terraform_configurations()
@@ -20,6 +20,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
20
20
  get_app_interface_vault_settings,
21
21
  )
22
22
  from reconcile.typed_queries.aws_vpc_requests import get_aws_vpc_requests
23
+ from reconcile.typed_queries.external_resources import get_settings
23
24
  from reconcile.typed_queries.github_orgs import get_github_orgs
24
25
  from reconcile.typed_queries.gitlab_instances import get_gitlab_instances
25
26
  from reconcile.utils import gql
@@ -31,7 +32,11 @@ from reconcile.utils.runtime.integration import (
31
32
  from reconcile.utils.secret_reader import create_secret_reader
32
33
  from reconcile.utils.semver_helper import make_semver
33
34
  from reconcile.utils.terraform_client import TerraformClient
34
- from reconcile.utils.terrascript_aws_client import TerrascriptClient
35
+ from reconcile.utils.terrascript_aws_client import (
36
+ VPC_REQUEST_DEFAULT_PRIVATE_SUBNET_TAGS,
37
+ VPC_REQUEST_DEFAULT_PUBLIC_SUBNET_TAGS,
38
+ TerrascriptClient,
39
+ )
35
40
  from reconcile.utils.vcs import VCS
36
41
 
37
42
  QONTRACT_INTEGRATION = "terraform_vpc_resources"
@@ -89,15 +94,32 @@ class TerraformVpcResources(QontractReconcileIntegration[TerraformVpcResourcesPa
89
94
  public_subnets = outputs_per_account.get(
90
95
  f"{request.identifier}-public_subnets", {}
91
96
  ).get("value", [])
97
+ availability_zones: list[str] = []
98
+
99
+ if request.subnets:
100
+ private_subnet_tags = VPC_REQUEST_DEFAULT_PRIVATE_SUBNET_TAGS | (
101
+ request.subnets.private_subnet_tags or {}
102
+ )
103
+ public_subnet_tags = VPC_REQUEST_DEFAULT_PUBLIC_SUBNET_TAGS | (
104
+ request.subnets.public_subnet_tags or {}
105
+ )
106
+ availability_zones = request.subnets.availability_zones or []
107
+ else:
108
+ private_subnet_tags = VPC_REQUEST_DEFAULT_PRIVATE_SUBNET_TAGS
109
+ public_subnet_tags = VPC_REQUEST_DEFAULT_PUBLIC_SUBNET_TAGS
92
110
 
93
111
  values = {
94
112
  "static": {
95
113
  "vpc_id": outputs_per_account.get(
96
114
  f"{request.identifier}-vpc_id", {}
97
115
  ).get("value"),
116
+ "vpc_tags": request.vpc_tags or {},
98
117
  "subnets": {
99
118
  "private": private_subnets,
100
119
  "public": public_subnets,
120
+ "private_subnet_tags": private_subnet_tags,
121
+ "public_subnet_tags": public_subnet_tags,
122
+ "availability_zones": availability_zones,
101
123
  },
102
124
  "account_name": request.account.name,
103
125
  "region": request.region,
@@ -140,13 +162,21 @@ class TerraformVpcResources(QontractReconcileIntegration[TerraformVpcResourcesPa
140
162
  logging.debug("No VPC requests found, nothing to do.")
141
163
  sys.exit(ExitCodes.SUCCESS)
142
164
 
143
- accounts_untyped: list[dict] = [acc.dict(by_alias=True) for acc in accounts]
165
+ accounts_untyped: list[dict] = [
166
+ acc.model_dump(by_alias=True) for acc in accounts
167
+ ]
168
+ try:
169
+ default_tags = get_settings().default_tags
170
+ except ValueError:
171
+ # no external resources settings found
172
+ default_tags = None
144
173
  with TerrascriptClient(
145
174
  integration=QONTRACT_INTEGRATION,
146
175
  integration_prefix=QONTRACT_TF_PREFIX,
147
176
  thread_pool_size=thread_pool_size,
148
177
  accounts=accounts_untyped,
149
178
  secret_reader=secret_reader,
179
+ default_tags=default_tags,
150
180
  ) as ts_client:
151
181
  ts_client.populate_vpc_requests(data, AWS_PROVIDER_VERSION)
152
182
 
@@ -0,0 +1,10 @@
1
+ from reconcile.gql_definitions.common.app_interface_roles import (
2
+ RoleV1,
3
+ query,
4
+ )
5
+ from reconcile.utils import gql
6
+
7
+
8
+ def get_app_interface_roles() -> list[RoleV1]:
9
+ data = query(gql.get_api().query)
10
+ return list(data.roles or [])
@@ -0,0 +1,41 @@
1
+ import json
2
+ from collections.abc import Mapping
3
+
4
+ from reconcile.gql_definitions.fragments.aws_organization import (
5
+ AWSOrganization,
6
+ )
7
+
8
+
9
+ def get_aws_account_tags(
10
+ organization: AWSOrganization | Mapping | None,
11
+ ) -> dict[str, str]:
12
+ """
13
+ Get AWS account tags by merging payer account tags
14
+
15
+ Args:
16
+ organization: AWSOrganization | Mapping | None - The organization object from which to extract tags.
17
+
18
+ Returns:
19
+ dict[str, str]: A dictionary containing the merged tags from the payer account and the account itself.
20
+ """
21
+ if organization is None:
22
+ return {}
23
+
24
+ match organization:
25
+ case AWSOrganization():
26
+ payer_account_tags = (
27
+ organization.payer_account.organization_account_tags or {}
28
+ )
29
+ account_tags = organization.tags or {}
30
+ case Mapping():
31
+ payer_account_tags = organization.get("payerAccount", {}).get(
32
+ "organizationAccountTags", {}
33
+ )
34
+ if isinstance(payer_account_tags, str):
35
+ payer_account_tags = json.loads(payer_account_tags)
36
+
37
+ account_tags = organization.get("tags", {})
38
+ if isinstance(account_tags, str):
39
+ account_tags = json.loads(account_tags)
40
+
41
+ return payer_account_tags | account_tags
@@ -6,7 +6,7 @@ from reconcile.utils.gql import GqlApi
6
6
 
7
7
  class App(BaseModel):
8
8
  name: str
9
- parent_app_name: str | None
9
+ parent_app_name: str | None = None
10
10
 
11
11
 
12
12
  def get_app_names(
@@ -13,7 +13,7 @@ class CostNamespace(BaseModel, frozen=True):
13
13
  labels: CostNamespaceLabels
14
14
  app_name: str
15
15
  cluster_name: str
16
- cluster_external_id: str | None
16
+ cluster_external_id: str | None = None
17
17
 
18
18
 
19
19
  def get_cost_namespaces(
@@ -32,7 +32,7 @@ def get_cost_namespaces(
32
32
  return [
33
33
  CostNamespace(
34
34
  name=namespace.name,
35
- labels=CostNamespaceLabels.parse_obj(namespace.labels or {}),
35
+ labels=CostNamespaceLabels.model_validate(namespace.labels or {}),
36
36
  app_name=namespace.app.name,
37
37
  cluster_name=namespace.cluster.name,
38
38
  cluster_external_id=namespace.cluster.spec.external_id
@@ -1,5 +1,4 @@
1
1
  import hashlib
2
- import json
3
2
  from collections.abc import Callable
4
3
  from threading import Lock
5
4
  from typing import Any
@@ -7,7 +6,6 @@ from typing import Any
7
6
  from jsonpath_ng.exceptions import JsonPathParserError
8
7
  from pydantic import (
9
8
  BaseModel,
10
- Extra,
11
9
  Field,
12
10
  Json,
13
11
  )
@@ -48,10 +46,16 @@ from reconcile.utils.exceptions import (
48
46
  AppInterfaceSettingsError,
49
47
  ParameterError,
50
48
  )
49
+ from reconcile.utils.json import json_dumps
51
50
  from reconcile.utils.jsonpath import parse_jsonpath
52
51
 
53
52
 
54
- class SaasResourceTemplateTarget(ConfiguredBaseModel):
53
+ class SaasResourceTemplateTarget(
54
+ ConfiguredBaseModel,
55
+ validate_by_alias=True,
56
+ # ignore `namespaceSelector` and 'provider' fields from the GQL schema
57
+ extra="ignore",
58
+ ):
55
59
  path: str | None = Field(..., alias="path")
56
60
  name: str | None = Field(..., alias="name")
57
61
  # the namespace must be required to fulfill the saas file schema (utils.saasherder.interface.SaasFile)
@@ -79,12 +83,8 @@ class SaasResourceTemplateTarget(ConfiguredBaseModel):
79
83
  digest_size=20,
80
84
  ).hexdigest()
81
85
 
82
- class Config:
83
- # ignore `namespaceSelector` and 'provider' fields from the GQL schema
84
- extra = Extra.ignore
85
-
86
86
 
87
- class SaasResourceTemplate(ConfiguredBaseModel):
87
+ class SaasResourceTemplate(ConfiguredBaseModel, validate_by_alias=True):
88
88
  name: str = Field(..., alias="name")
89
89
  url: str = Field(..., alias="url")
90
90
  path: str = Field(..., alias="path")
@@ -97,7 +97,7 @@ class SaasResourceTemplate(ConfiguredBaseModel):
97
97
  targets: list[SaasResourceTemplateTarget] = Field(..., alias="targets")
98
98
 
99
99
 
100
- class SaasFile(ConfiguredBaseModel):
100
+ class SaasFile(ConfiguredBaseModel, validate_by_alias=True):
101
101
  path: str = Field(..., alias="path")
102
102
  name: str = Field(..., alias="name")
103
103
  labels: Json | None = Field(..., alias="labels")
@@ -221,7 +221,7 @@ class SaasFileList:
221
221
  with self._namespaces_as_dict_lock:
222
222
  self._namespaces_as_dict_cache = {
223
223
  "namespace": [
224
- ns.dict(by_alias=True, exclude_none=True)
224
+ ns.model_dump(by_alias=True, exclude_none=True)
225
225
  for ns in self.namespaces
226
226
  ]
227
227
  }
@@ -283,7 +283,7 @@ class SaasFileList:
283
283
  if app_name and saas_file.app.name != app_name:
284
284
  continue
285
285
 
286
- sf = saas_file.copy(deep=True)
286
+ sf = saas_file.model_copy(deep=True)
287
287
  if env_name:
288
288
  for rt in sf.resource_templates[:]:
289
289
  for target in rt.targets[:]:
@@ -302,7 +302,7 @@ def convert_parameters_to_json_string(root: dict[str, Any]) -> dict[str, Any]:
302
302
  """Find all parameter occurrences and convert them to a json string."""
303
303
  for key, value in root.items():
304
304
  if key in {"parameters", "labels"}:
305
- root[key] = json.dumps(value) if value is not None else None
305
+ root[key] = json_dumps(value) if value is not None else None
306
306
  elif isinstance(value, dict):
307
307
  root[key] = convert_parameters_to_json_string(value)
308
308
  elif isinstance(value, list):
@@ -314,7 +314,7 @@ def convert_parameters_to_json_string(root: dict[str, Any]) -> dict[str, Any]:
314
314
 
315
315
 
316
316
  def export_model(model: BaseModel) -> dict[str, Any]:
317
- return convert_parameters_to_json_string(model.dict(by_alias=True))
317
+ return convert_parameters_to_json_string(model.model_dump(by_alias=True))
318
318
 
319
319
 
320
320
  def get_saas_files(
@@ -32,7 +32,7 @@ def get_selected_app_names(
32
32
  prefix = f"{namespace.app.parent_app.name}-"
33
33
  name = f"{prefix}{namespace.app.name}"
34
34
  selected_app_names.add(name)
35
- app = namespace.app.dict(by_alias=True)
35
+ app = namespace.app.model_dump(by_alias=True)
36
36
  app["name"] = name
37
37
  apps["apps"].append(app)
38
38
 
@@ -40,7 +40,7 @@ def get_selected_app_names(
40
40
  name = f"{namespace.app.name}-{child.name}"
41
41
  if name not in selected_app_names:
42
42
  selected_app_names.add(f"{namespace.app.name}-{child.name}")
43
- child_dict = child.dict(by_alias=True)
43
+ child_dict = child.model_dump(by_alias=True)
44
44
  child_dict["name"] = name
45
45
  apps["apps"].append(child_dict)
46
46
 
@@ -31,7 +31,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
31
31
 
32
32
 
33
33
  class UnleashTogglesIntegrationParams(PydanticRunParams):
34
- instance: str | None
34
+ instance: str | None = None
35
35
 
36
36
 
37
37
  def feature_toggle_equal(c: FeatureToggle, d: FeatureToggleUnleashV1) -> bool:
@@ -68,7 +68,9 @@ class UnleashTogglesIntegration(
68
68
  if not query_func:
69
69
  query_func = gql.get_api().query
70
70
  return {
71
- "toggles": [ft.dict() for ft in self.get_unleash_instances(query_func)],
71
+ "toggles": [
72
+ ft.model_dump() for ft in self.get_unleash_instances(query_func)
73
+ ],
72
74
  }
73
75
 
74
76
  def get_unleash_instances(
@@ -6,7 +6,7 @@ from typing import (
6
6
  )
7
7
 
8
8
  import requests
9
- from pydantic import BaseModel
9
+ from pydantic import BaseModel, ConfigDict
10
10
 
11
11
  from reconcile.gql_definitions.acs.acs_instances import AcsInstanceV1
12
12
  from reconcile.gql_definitions.acs.acs_instances import query as acs_instances_query
@@ -19,8 +19,11 @@ class AcsBaseApi(BaseModel):
19
19
  timeout: int = 30
20
20
  session: requests.Session = requests.Session()
21
21
 
22
- class Config:
23
- arbitrary_types_allowed = True
22
+ model_config = ConfigDict(
23
+ validate_by_name=True,
24
+ validate_by_alias=True,
25
+ arbitrary_types_allowed=True,
26
+ )
24
27
 
25
28
  def __enter__(self) -> Self:
26
29
  return self
@@ -11,7 +11,7 @@ class Scope(BaseModel):
11
11
  """
12
12
 
13
13
  cluster: str
14
- namespace: str | None
14
+ namespace: str | None = None
15
15
 
16
16
 
17
17
  class PolicyCondition(BaseModel):
@@ -23,7 +23,7 @@ class PolicyCondition(BaseModel):
23
23
  """
24
24
 
25
25
  field_name: str
26
- negate: bool | None
26
+ negate: bool | None = None
27
27
  values: list[str]
28
28
 
29
29
 
@@ -1,8 +1,9 @@
1
- import json
2
1
  import logging
3
2
  from collections.abc import Callable, KeysView
4
3
  from typing import Any, TypedDict
5
4
 
5
+ from reconcile.utils.json import json_dumps
6
+
6
7
  Action = Callable[[Any, list[Any]], bool]
7
8
  Cond = Callable[[Any], bool]
8
9
 
@@ -89,11 +90,11 @@ class AggregatedList:
89
90
  return list(self._dict.values())
90
91
 
91
92
  def to_json(self) -> str:
92
- return json.dumps(self.dump(), indent=4)
93
+ return json_dumps(self.dump(), indent=4)
93
94
 
94
95
  @staticmethod
95
96
  def hash_params(params: Any) -> int:
96
- return hash(json.dumps(params, sort_keys=True))
97
+ return hash(json_dumps(params))
97
98
 
98
99
 
99
100
  class AggregatedDiffRunner:
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import logging
4
4
  import operator
5
5
  import os
6
- import re
7
6
  from functools import lru_cache
8
7
  from threading import Lock
9
8
  from typing import (
@@ -25,6 +24,7 @@ import reconcile.utils.lean_terraform_client as terraform
25
24
  from reconcile.utils.secret_reader import SecretReader, SecretReaderBase
26
25
 
27
26
  if TYPE_CHECKING:
27
+ import re
28
28
  from collections.abc import (
29
29
  Iterable,
30
30
  Iterator,
@@ -1074,28 +1074,40 @@ class AWSApi:
1074
1074
  return [rt["RouteTableId"] for rt in vpc_route_tables]
1075
1075
 
1076
1076
  @staticmethod
1077
- def _filter_amis(
1078
- images: Iterable[ImageTypeDef], regex: str
1079
- ) -> list[dict[str, Any]]:
1080
- results = []
1081
- pattern = re.compile(regex)
1082
- for i in images:
1083
- if not re.search(pattern, i["Name"]):
1084
- continue
1085
- if i["State"] != "available":
1086
- continue
1087
- item = {"image_id": i["ImageId"], "tags": i.get("Tags", [])}
1088
- results.append(item)
1077
+ def normalize_tags(tags: Iterable[TagTypeDef]) -> dict[str, str]:
1078
+ return {tag["Key"]: tag["Value"] for tag in tags}
1089
1079
 
1090
- return results
1080
+ @staticmethod
1081
+ def _filter_amis(
1082
+ images: Iterable[ImageTypeDef],
1083
+ regex: re.Pattern,
1084
+ ) -> dict[str, dict[str, str]]:
1085
+ return {
1086
+ image["ImageId"]: AWSApi.normalize_tags(image.get("Tags", []))
1087
+ for image in images
1088
+ if regex.search(image["Name"]) and image["State"] == "available"
1089
+ }
1091
1090
 
1092
1091
  def get_amis_details(
1093
1092
  self,
1094
1093
  account: Mapping[str, Any],
1095
1094
  owner_account: Mapping[str, Any],
1096
- regex: str,
1095
+ regex: re.Pattern,
1097
1096
  region: str | None = None,
1098
- ) -> list[dict[str, Any]]:
1097
+ ) -> dict[str, dict[str, str]]:
1098
+ """
1099
+ Get AMI details for an account, find AMI name matches regex and state is available.
1100
+ Return ImageId and normalized tags.
1101
+
1102
+ Args:
1103
+ account: AWS account
1104
+ owner_account: AMI owner AWS account uid
1105
+ regex: regex to filter AMI name
1106
+ region: AWS account region
1107
+
1108
+ Returns:
1109
+ dict[str, dict[str, str]]: Key is AMI ImageId, value is AMI normalized tags.
1110
+ """
1099
1111
  ec2 = self._account_ec2_client(account["name"], region_name=region)
1100
1112
  images = self.get_account_amis(ec2, owner=owner_account["uid"])
1101
1113
  return self._filter_amis(images, regex)
@@ -1175,12 +1187,31 @@ class AWSApi:
1175
1187
  client = self._account_cloudwatch_client(account_name, region_name=region_name)
1176
1188
  client.delete_log_group(logGroupName=group_name)
1177
1189
 
1178
- def create_tag(
1179
- self, account: Mapping[str, Any], resource_id: str, tag: Mapping[str, str]
1190
+ def create_tags(
1191
+ self,
1192
+ account: Mapping[str, Any],
1193
+ resource_id: str,
1194
+ tags: Mapping[str, str],
1180
1195
  ) -> None:
1196
+ """
1197
+ Create tags on EC2 resources (AMI)
1198
+
1199
+ Args:
1200
+ account: AWS account
1201
+ resource_id: AWS resource id
1202
+ tags: tags to update
1203
+
1204
+ Returns:
1205
+ None
1206
+ """
1181
1207
  ec2 = self._account_ec2_client(account["name"])
1182
- tag_type_def: TagTypeDef = {"Key": tag["Key"], "Value": tag["Value"]}
1183
- ec2.create_tags(Resources=[resource_id], Tags=[tag_type_def])
1208
+ formatted_tags: list[TagTypeDef] = [
1209
+ {"Key": k, "Value": v} for k, v in tags.items()
1210
+ ]
1211
+ ec2.create_tags(
1212
+ Resources=[resource_id],
1213
+ Tags=formatted_tags,
1214
+ )
1184
1215
 
1185
1216
  def get_alb_network_interface_ips(
1186
1217
  self, account: awsh.Account, service_name: str