qontract-reconcile 0.10.2.dev349__py3-none-any.whl → 0.10.2.dev414__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 (356) hide show
  1. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +12 -11
  2. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +356 -350
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +15 -12
  5. reconcile/aus/base.py +26 -27
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +1 -1
  8. reconcile/automated_actions/config/integration.py +15 -3
  9. reconcile/aws_account_manager/integration.py +8 -8
  10. reconcile/aws_account_manager/reconciler.py +3 -3
  11. reconcile/aws_ami_cleanup/integration.py +8 -12
  12. reconcile/aws_ami_share.py +69 -62
  13. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  14. reconcile/aws_ecr_image_pull_secrets.py +2 -2
  15. reconcile/aws_iam_keys.py +7 -41
  16. reconcile/aws_saml_idp/integration.py +12 -4
  17. reconcile/aws_saml_roles/integration.py +32 -25
  18. reconcile/aws_version_sync/integration.py +6 -12
  19. reconcile/change_owners/bundle.py +3 -3
  20. reconcile/change_owners/change_log_tracking.py +3 -2
  21. reconcile/change_owners/change_owners.py +1 -1
  22. reconcile/change_owners/diff.py +2 -4
  23. reconcile/checkpoint.py +11 -3
  24. reconcile/cli.py +33 -8
  25. reconcile/dashdotdb_dora.py +5 -12
  26. reconcile/dashdotdb_slo.py +1 -1
  27. reconcile/database_access_manager.py +123 -117
  28. reconcile/dynatrace_token_provider/integration.py +1 -1
  29. reconcile/endpoints_discovery/integration.py +4 -1
  30. reconcile/endpoints_discovery/merge_request.py +1 -1
  31. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  32. reconcile/external_resources/factories.py +5 -12
  33. reconcile/external_resources/integration.py +1 -1
  34. reconcile/external_resources/manager.py +24 -10
  35. reconcile/external_resources/meta.py +0 -1
  36. reconcile/external_resources/metrics.py +1 -1
  37. reconcile/external_resources/model.py +13 -13
  38. reconcile/external_resources/reconciler.py +7 -4
  39. reconcile/external_resources/secrets_sync.py +6 -8
  40. reconcile/external_resources/state.py +60 -17
  41. reconcile/fleet_labeler/integration.py +1 -1
  42. reconcile/gabi_authorized_users.py +8 -5
  43. reconcile/gcp_image_mirror.py +2 -2
  44. reconcile/github_org.py +1 -1
  45. reconcile/github_owners.py +4 -0
  46. reconcile/gitlab_housekeeping.py +13 -15
  47. reconcile/gitlab_members.py +6 -12
  48. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  49. reconcile/gitlab_owners.py +15 -11
  50. reconcile/gitlab_permissions.py +8 -12
  51. reconcile/glitchtip_project_alerts/integration.py +3 -1
  52. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  53. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  54. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  55. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  56. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  57. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  58. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  59. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  60. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  61. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
  62. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  63. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  64. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  65. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  66. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  67. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  68. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  69. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  70. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  71. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  72. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  73. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  74. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  75. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  76. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  77. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  78. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  79. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  80. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  81. reconcile/gql_definitions/common/apps.py +5 -5
  82. reconcile/gql_definitions/common/aws_vpc_requests.py +15 -5
  83. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  84. reconcile/gql_definitions/common/clusters.py +7 -5
  85. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  86. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  87. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  88. reconcile/gql_definitions/common/github_orgs.py +5 -5
  89. reconcile/gql_definitions/common/jira_settings.py +5 -5
  90. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  91. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  92. reconcile/gql_definitions/common/namespaces.py +5 -5
  93. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  94. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  95. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  96. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  97. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  98. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  99. reconcile/gql_definitions/common/quay_instances.py +5 -5
  100. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  101. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  102. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  103. reconcile/gql_definitions/common/saas_files.py +5 -5
  104. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  105. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  106. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  107. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  108. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  109. reconcile/gql_definitions/common/users.py +5 -5
  110. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  111. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  112. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  113. reconcile/gql_definitions/cost_report/settings.py +5 -5
  114. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  115. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  116. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  117. reconcile/gql_definitions/email_sender/apps.py +5 -5
  118. reconcile/gql_definitions/email_sender/emails.py +5 -5
  119. reconcile/gql_definitions/email_sender/users.py +5 -5
  120. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  121. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  122. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  123. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +89 -6
  124. reconcile/gql_definitions/external_resources/external_resources_settings.py +7 -5
  125. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  126. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  127. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  128. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  129. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  130. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  131. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  132. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  133. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_vpc_request.py +7 -5
  135. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  136. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  137. reconcile/gql_definitions/fragments/disable.py +5 -5
  138. reconcile/gql_definitions/fragments/email_service.py +5 -5
  139. reconcile/gql_definitions/fragments/email_user.py +5 -5
  140. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  141. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  142. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  143. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  144. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  145. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  146. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  147. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  148. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  149. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  150. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  151. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  152. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  153. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  154. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  155. reconcile/gql_definitions/fragments/user.py +5 -5
  156. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  157. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  158. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  159. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  160. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  161. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  162. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  163. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  164. reconcile/gql_definitions/integrations/integrations.py +5 -5
  165. reconcile/gql_definitions/introspection.json +2137 -1053
  166. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  167. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  168. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  169. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  170. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  171. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  172. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  173. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  174. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  175. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  176. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  177. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  178. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  179. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  180. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  181. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  182. reconcile/gql_definitions/rhcs/certs.py +5 -5
  183. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  184. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  185. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  186. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  187. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  188. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  189. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  190. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  191. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  192. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  193. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  194. reconcile/gql_definitions/status_board/status_board.py +5 -5
  195. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  196. reconcile/gql_definitions/templating/template_collection.py +5 -5
  197. reconcile/gql_definitions/templating/templates.py +5 -5
  198. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  199. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  200. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  201. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  202. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  203. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  204. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  205. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  206. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  207. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +38 -6
  208. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  209. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  210. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  211. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  212. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
  213. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  214. reconcile/integrations_manager.py +3 -3
  215. reconcile/jenkins_worker_fleets.py +10 -8
  216. reconcile/jira_permissions_validator.py +237 -122
  217. reconcile/ldap_groups/integration.py +1 -1
  218. reconcile/ocm/types.py +35 -56
  219. reconcile/ocm_aws_infrastructure_access.py +1 -1
  220. reconcile/ocm_clusters.py +4 -4
  221. reconcile/ocm_labels/integration.py +3 -2
  222. reconcile/ocm_machine_pools.py +23 -23
  223. reconcile/openshift_base.py +53 -2
  224. reconcile/openshift_cluster_bots.py +3 -2
  225. reconcile/openshift_namespace_labels.py +1 -1
  226. reconcile/openshift_namespaces.py +97 -101
  227. reconcile/openshift_resources_base.py +6 -2
  228. reconcile/openshift_rhcs_certs.py +5 -5
  229. reconcile/openshift_rolebindings.py +7 -11
  230. reconcile/openshift_saas_deploy.py +6 -7
  231. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  232. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  233. reconcile/openshift_serviceaccount_tokens.py +2 -2
  234. reconcile/openshift_upgrade_watcher.py +4 -4
  235. reconcile/oum/labelset.py +5 -3
  236. reconcile/oum/models.py +1 -4
  237. reconcile/prometheus_rules_tester/integration.py +3 -3
  238. reconcile/quay_mirror.py +1 -1
  239. reconcile/queries.py +131 -1
  240. reconcile/rhidp/common.py +3 -5
  241. reconcile/rhidp/sso_client/base.py +1 -1
  242. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  243. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  244. reconcile/skupper_network/integration.py +2 -2
  245. reconcile/slack_usergroups.py +35 -14
  246. reconcile/sql_query.py +1 -0
  247. reconcile/status_board.py +6 -6
  248. reconcile/statuspage/atlassian.py +7 -7
  249. reconcile/statuspage/integrations/maintenances.py +4 -3
  250. reconcile/statuspage/page.py +4 -9
  251. reconcile/statuspage/status.py +5 -8
  252. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
  253. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  254. reconcile/templating/lib/rendering.py +3 -3
  255. reconcile/templating/renderer.py +4 -3
  256. reconcile/terraform_aws_route53.py +7 -1
  257. reconcile/terraform_cloudflare_dns.py +3 -3
  258. reconcile/terraform_cloudflare_resources.py +5 -5
  259. reconcile/terraform_cloudflare_users.py +3 -2
  260. reconcile/terraform_init/integration.py +187 -23
  261. reconcile/terraform_repo.py +16 -12
  262. reconcile/terraform_resources.py +17 -7
  263. reconcile/terraform_tgw_attachments.py +27 -19
  264. reconcile/terraform_users.py +7 -0
  265. reconcile/terraform_vpc_peerings.py +14 -3
  266. reconcile/terraform_vpc_resources/integration.py +10 -1
  267. reconcile/typed_queries/aws_account_tags.py +41 -0
  268. reconcile/typed_queries/cost_report/app_names.py +1 -1
  269. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  270. reconcile/typed_queries/saas_files.py +13 -13
  271. reconcile/typed_queries/status_board.py +2 -2
  272. reconcile/unleash_feature_toggles/integration.py +4 -2
  273. reconcile/utils/acs/base.py +6 -3
  274. reconcile/utils/acs/policies.py +2 -2
  275. reconcile/utils/aggregated_list.py +4 -3
  276. reconcile/utils/aws_api.py +51 -54
  277. reconcile/utils/aws_api_typed/api.py +38 -9
  278. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  279. reconcile/utils/aws_api_typed/logs.py +73 -0
  280. reconcile/utils/aws_api_typed/organization.py +4 -2
  281. reconcile/utils/datetime_util.py +67 -0
  282. reconcile/utils/deadmanssnitch_api.py +1 -1
  283. reconcile/utils/differ.py +2 -3
  284. reconcile/utils/early_exit_cache.py +11 -12
  285. reconcile/utils/expiration.py +7 -3
  286. reconcile/utils/external_resource_spec.py +24 -1
  287. reconcile/utils/filtering.py +1 -1
  288. reconcile/utils/gitlab_api.py +7 -5
  289. reconcile/utils/glitchtip/client.py +6 -2
  290. reconcile/utils/glitchtip/models.py +25 -28
  291. reconcile/utils/gql.py +4 -7
  292. reconcile/utils/helm.py +2 -1
  293. reconcile/utils/helpers.py +1 -1
  294. reconcile/utils/instrumented_wrappers.py +1 -1
  295. reconcile/utils/internal_groups/client.py +2 -2
  296. reconcile/utils/internal_groups/models.py +8 -17
  297. reconcile/utils/jinja2/utils.py +6 -101
  298. reconcile/utils/jira_client.py +82 -63
  299. reconcile/utils/jjb_client.py +9 -12
  300. reconcile/utils/jobcontroller/controller.py +1 -1
  301. reconcile/utils/jobcontroller/models.py +17 -1
  302. reconcile/utils/json.py +70 -0
  303. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  304. reconcile/utils/membershipsources/models.py +16 -23
  305. reconcile/utils/membershipsources/resolver.py +4 -2
  306. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  307. reconcile/utils/merge_request_manager/parser.py +6 -6
  308. reconcile/utils/metrics.py +5 -5
  309. reconcile/utils/models.py +304 -82
  310. reconcile/utils/mr/app_interface_reporter.py +2 -2
  311. reconcile/utils/mr/base.py +2 -2
  312. reconcile/utils/mr/notificator.py +3 -3
  313. reconcile/utils/mr/update_access_report_base.py +3 -4
  314. reconcile/utils/mr/user_maintenance.py +3 -2
  315. reconcile/utils/oc.py +118 -97
  316. reconcile/utils/oc_filters.py +3 -3
  317. reconcile/utils/ocm/addons.py +0 -1
  318. reconcile/utils/ocm/base.py +17 -20
  319. reconcile/utils/ocm/cluster_groups.py +1 -1
  320. reconcile/utils/ocm/identity_providers.py +2 -2
  321. reconcile/utils/ocm/labels.py +1 -1
  322. reconcile/utils/ocm/products.py +9 -3
  323. reconcile/utils/ocm/search_filters.py +3 -6
  324. reconcile/utils/ocm/service_log.py +4 -6
  325. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  326. reconcile/utils/openshift_resource.py +10 -5
  327. reconcile/utils/output.py +3 -2
  328. reconcile/utils/pagerduty_api.py +10 -7
  329. reconcile/utils/promotion_state.py +6 -11
  330. reconcile/utils/raw_github_api.py +1 -1
  331. reconcile/utils/rhcsv2_certs.py +1 -4
  332. reconcile/utils/runtime/integration.py +2 -3
  333. reconcile/utils/runtime/runner.py +2 -2
  334. reconcile/utils/saasherder/interfaces.py +13 -20
  335. reconcile/utils/saasherder/models.py +25 -21
  336. reconcile/utils/saasherder/saasherder.py +35 -24
  337. reconcile/utils/slack_api.py +26 -4
  338. reconcile/utils/sloth.py +171 -2
  339. reconcile/utils/sqs_gateway.py +2 -1
  340. reconcile/utils/state.py +2 -1
  341. reconcile/utils/structs.py +1 -1
  342. reconcile/utils/terraform_client.py +5 -4
  343. reconcile/utils/terrascript_aws_client.py +171 -114
  344. reconcile/utils/unleash/server.py +2 -8
  345. reconcile/utils/vault.py +5 -12
  346. reconcile/utils/vcs.py +8 -8
  347. reconcile/vault_replication.py +107 -42
  348. tools/app_interface_reporter.py +4 -4
  349. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  350. tools/cli_commands/cost_report/view.py +7 -6
  351. tools/cli_commands/erv2.py +3 -1
  352. tools/cli_commands/systems_and_tools.py +5 -1
  353. tools/qontract_cli.py +31 -18
  354. tools/template_validation.py +3 -1
  355. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
  356. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
@@ -11,7 +11,7 @@ from typing import (
11
11
  TypedDict,
12
12
  )
13
13
 
14
- from pydantic import BaseModel
14
+ from pydantic import BaseModel, Field
15
15
  from sretoolbox.container.image import Image
16
16
 
17
17
  from reconcile import openshift_base, queries
@@ -61,17 +61,19 @@ def get_database_access_namespaces(
61
61
  return query(query_func).namespaces_v1 or []
62
62
 
63
63
 
64
- class DatabaseConnectionParameters(BaseModel):
65
- host: str
66
- port: str
67
- user: str
68
- password: str
69
- database: str
64
+ class DatabaseConnectionParameters(
65
+ BaseModel, validate_by_name=True, validate_by_alias=True
66
+ ):
67
+ host: str = Field(..., alias="db.host")
68
+ port: str = Field(..., alias="db.port")
69
+ user: str = Field(..., alias="db.user")
70
+ password: str = Field(..., alias="db.password")
71
+ database: str = Field(..., alias="db.name")
70
72
 
71
73
 
72
74
  class PSQLScriptGenerator(BaseModel):
73
75
  db_access: DatabaseAccessV1
74
- current_db_access: DatabaseAccessV1 | None
76
+ current_db_access: DatabaseAccessV1 | None = None
75
77
  connection_parameter: DatabaseConnectionParameters
76
78
  admin_connection_parameter: DatabaseConnectionParameters
77
79
  engine: str
@@ -225,7 +227,7 @@ def get_db_engine(resource: NamespaceTerraformResourceRDSV1) -> str:
225
227
 
226
228
  class JobData(BaseModel):
227
229
  engine: str
228
- name_suffix: str
230
+ name: str
229
231
  image: str
230
232
  service_account_name: str
231
233
  rds_admin_secret_name: str
@@ -234,7 +236,7 @@ class JobData(BaseModel):
234
236
 
235
237
 
236
238
  def get_job_spec(job_data: JobData) -> OpenshiftResource:
237
- job_name = f"dbam-{job_data.name_suffix}"
239
+ job_name = job_data.name
238
240
 
239
241
  image_tag = Image(job_data.image).tag
240
242
  image_pull_policy = "Always" if image_tag == "latest" else "IfNotPresent"
@@ -391,14 +393,6 @@ def get_service_account_spec(name: str) -> OpenshiftResource:
391
393
  )
392
394
 
393
395
 
394
- class DBAMResource(BaseModel):
395
- resource: OpenshiftResource
396
- clean_up: bool
397
-
398
- class Config:
399
- arbitrary_types_allowed = True
400
-
401
-
402
396
  class JobStatusCondition(BaseModel):
403
397
  type: str
404
398
 
@@ -424,18 +418,12 @@ def _populate_resources(
424
418
  user_connection: DatabaseConnectionParameters,
425
419
  admin_connection: DatabaseConnectionParameters,
426
420
  current_db_access: DatabaseAccessV1 | None = None,
427
- ) -> list[DBAMResource]:
421
+ ) -> list[OpenshiftResource]:
428
422
  if user_connection.database == admin_connection.database:
429
423
  raise ValueError(f"Can not use default database {admin_connection.database}")
430
424
 
431
- managed_resources: list[DBAMResource] = []
432
425
  # create service account
433
- managed_resources.append(
434
- DBAMResource(
435
- resource=get_service_account_spec(resource_prefix),
436
- clean_up=True,
437
- )
438
- )
426
+ service_account_resource = get_service_account_spec(resource_prefix)
439
427
 
440
428
  # create script secret
441
429
  generator = PSQLScriptGenerator(
@@ -446,74 +434,107 @@ def _populate_resources(
446
434
  engine=engine,
447
435
  )
448
436
  script_secret_name = f"{resource_prefix}-script"
449
- managed_resources.extend([
450
- DBAMResource(
451
- resource=generate_script_secret_spec(
452
- script_secret_name,
453
- generator.generate_script(),
454
- ),
455
- clean_up=True,
456
- ),
457
- # create user secret
458
- DBAMResource(
459
- resource=generate_user_secret_spec(resource_prefix, user_connection),
460
- clean_up=False,
461
- ),
462
- ])
437
+ secret_script_resource = generate_script_secret_spec(
438
+ script_secret_name,
439
+ generator.generate_script(),
440
+ )
441
+
442
+ # create user secret
443
+ user_secret_resource = generate_user_secret_spec(
444
+ resource_prefix,
445
+ user_connection,
446
+ )
447
+
463
448
  # create pull secret
464
- labels = pull_secret["labels"] or {}
465
- pull_secret_resources = orb.fetch_provider_vault_secret(
449
+ pull_secret_resource = orb.fetch_provider_vault_secret(
466
450
  path=pull_secret["path"],
467
451
  version=pull_secret["version"],
468
452
  name=f"{resource_prefix}-pull-secret",
469
- labels=labels,
453
+ labels=pull_secret["labels"] or {},
470
454
  annotations=pull_secret["annotations"] or {},
471
455
  type=pull_secret["type"],
472
456
  integration=QONTRACT_INTEGRATION,
473
457
  integration_version=QONTRACT_INTEGRATION_VERSION,
474
458
  settings=settings,
475
459
  )
476
- managed_resources.extend([
477
- DBAMResource(resource=pull_secret_resources, clean_up=True),
478
- # create job
479
- DBAMResource(
480
- resource=get_job_spec(
481
- JobData(
482
- engine=engine,
483
- name_suffix=db_access.name,
484
- image=job_image,
485
- service_account_name=resource_prefix,
486
- rds_admin_secret_name=admin_secret_name,
487
- script_secret_name=script_secret_name,
488
- pull_secret=f"{resource_prefix}-pull-secret",
489
- )
490
- ),
491
- clean_up=True,
492
- ),
493
- ])
494
460
 
495
- return managed_resources
461
+ job_resource = get_job_spec(
462
+ JobData(
463
+ engine=engine,
464
+ name=resource_prefix,
465
+ image=job_image,
466
+ service_account_name=resource_prefix,
467
+ rds_admin_secret_name=admin_secret_name,
468
+ script_secret_name=script_secret_name,
469
+ pull_secret=f"{resource_prefix}-pull-secret",
470
+ )
471
+ )
472
+
473
+ return [
474
+ service_account_resource,
475
+ secret_script_resource,
476
+ user_secret_resource,
477
+ pull_secret_resource,
478
+ job_resource,
479
+ ]
496
480
 
497
481
 
498
482
  def _generate_password() -> str:
499
483
  return "".join(choices(ascii_letters + digits, k=32))
500
484
 
501
485
 
502
- class _DBDonnections(TypedDict):
486
+ class DBConnections(TypedDict):
503
487
  user: DatabaseConnectionParameters
504
488
  admin: DatabaseConnectionParameters
505
489
 
506
490
 
491
+ def _decode_secret_value(value: str) -> str:
492
+ return base64.b64decode(value).decode("utf-8")
493
+
494
+
495
+ def _build_user_connection(
496
+ db_access: DatabaseAccessV1,
497
+ admin_secret: dict[str, Any],
498
+ user_secret: dict[str, Any],
499
+ ) -> DatabaseConnectionParameters:
500
+ """
501
+ Build user connection parameters.
502
+
503
+ If user secret exists, use values from it as that's the one used to provision new database user.
504
+ Otherwise, generate a new password, this info will be saved as cluster secret.
505
+ After job completes, the secret will be saved to vault then deleted from the cluster.
506
+
507
+ Args:
508
+ db_access (DatabaseAccessV1): Database access definition from app-interface.
509
+ admin_secret (dict[str, Any]): Admin secret fetched from the cluster.
510
+ user_secret (dict[str, Any]): User secret fetched from the cluster, may be empty if not exists.
511
+ Returns:
512
+ DatabaseConnectionParameters: Connection parameters for the database user.
513
+ """
514
+ if user_secret:
515
+ return DatabaseConnectionParameters(
516
+ host=_decode_secret_value(user_secret["data"]["db.host"]),
517
+ port=_decode_secret_value(user_secret["data"]["db.port"]),
518
+ user=_decode_secret_value(user_secret["data"]["db.user"]),
519
+ password=_decode_secret_value(user_secret["data"]["db.password"]),
520
+ database=_decode_secret_value(user_secret["data"]["db.name"]),
521
+ )
522
+ return DatabaseConnectionParameters(
523
+ host=_decode_secret_value(admin_secret["data"]["db.host"]),
524
+ port=_decode_secret_value(admin_secret["data"]["db.port"]),
525
+ user=db_access.username,
526
+ password=_generate_password(),
527
+ database=db_access.database,
528
+ )
529
+
530
+
507
531
  def _create_database_connection_parameter(
508
532
  db_access: DatabaseAccessV1,
509
533
  namespace_name: str,
510
534
  oc: OCClient,
511
535
  admin_secret_name: str,
512
536
  user_secret_name: str,
513
- ) -> _DBDonnections:
514
- def _decode_secret_value(value: str) -> str:
515
- return base64.b64decode(value).decode("utf-8")
516
-
537
+ ) -> DBConnections:
517
538
  user_secret = oc.get(
518
539
  namespace_name,
519
540
  "Secret",
@@ -526,26 +547,11 @@ def _create_database_connection_parameter(
526
547
  admin_secret_name,
527
548
  allow_not_found=False,
528
549
  )
529
-
530
- if user_secret:
531
- password = _decode_secret_value(user_secret["data"]["db.password"])
532
- host = _decode_secret_value(user_secret["data"]["db.host"])
533
- user = _decode_secret_value(user_secret["data"]["db.user"])
534
- port = _decode_secret_value(user_secret["data"]["db.port"])
535
- database = _decode_secret_value(user_secret["data"]["db.name"])
536
- else:
537
- host = _decode_secret_value(admin_secret["data"]["db.host"])
538
- port = _decode_secret_value(admin_secret["data"]["db.port"])
539
- user = db_access.username
540
- password = _generate_password()
541
- database = db_access.database
542
- return _DBDonnections(
543
- user=DatabaseConnectionParameters(
544
- host=host,
545
- port=port,
546
- user=user,
547
- password=password,
548
- database=database,
550
+ return DBConnections(
551
+ user=_build_user_connection(
552
+ db_access=db_access,
553
+ admin_secret=admin_secret,
554
+ user_secret=user_secret,
549
555
  ),
550
556
  admin=DatabaseConnectionParameters(
551
557
  host=_decode_secret_value(admin_secret["data"]["db.host"]),
@@ -557,10 +563,10 @@ def _create_database_connection_parameter(
557
563
  )
558
564
 
559
565
 
560
- def _db_access_acccess_is_valid(db_acces: DatabaseAccessV1) -> bool:
566
+ def _db_access_access_is_valid(db_access: DatabaseAccessV1) -> bool:
561
567
  found_schema: set[str] = set()
562
568
 
563
- for schema in db_acces.access or []:
569
+ for schema in db_access.access or []:
564
570
  if schema.target.dbschema in found_schema:
565
571
  return False
566
572
  found_schema.add(schema.target.dbschema)
@@ -575,6 +581,7 @@ class JobFailedError(Exception):
575
581
  def _process_db_access(
576
582
  dry_run: bool,
577
583
  state: State,
584
+ state_key: str,
578
585
  db_access: DatabaseAccessV1,
579
586
  namespace: NamespaceV1,
580
587
  admin_secret_name: str,
@@ -583,13 +590,13 @@ def _process_db_access(
583
590
  vault_output_path: str,
584
591
  vault_client: VaultClient,
585
592
  ) -> None:
586
- if not _db_access_acccess_is_valid(db_access):
593
+ if not _db_access_access_is_valid(db_access):
587
594
  raise ValueError("Duplicate schema in access list.")
588
595
 
589
596
  current_db_access: DatabaseAccessV1 | None = None
590
- if state.exists(db_access.name):
591
- current_state = state.get(db_access.name)
592
- if current_state == db_access.dict(by_alias=True):
597
+ if state.exists(state_key):
598
+ current_state = state.get(state_key)
599
+ if current_state == db_access.model_dump(by_alias=True):
593
600
  return
594
601
  current_db_access = DatabaseAccessV1(**current_state)
595
602
  if current_db_access.database != db_access.database:
@@ -600,9 +607,9 @@ def _process_db_access(
600
607
  cluster_name = namespace.cluster.name
601
608
  namespace_name = namespace.name
602
609
 
603
- resource_prefix = f"dbam-{db_access.name}"
610
+ resource_prefix = f"dbam-{state_key.replace('/', '-')}"
604
611
  with OC_Map(
605
- clusters=[namespace.cluster.dict(by_alias=True)],
612
+ clusters=[namespace.cluster.model_dump(by_alias=True)],
606
613
  integration=QONTRACT_INTEGRATION,
607
614
  settings=settings,
608
615
  ) as oc_map:
@@ -613,7 +620,7 @@ def _process_db_access(
613
620
  namespace_name,
614
621
  oc,
615
622
  admin_secret_name,
616
- resource_prefix,
623
+ user_secret_name=resource_prefix,
617
624
  )
618
625
 
619
626
  sql_query_settings = settings.get("sqlQuery")
@@ -642,7 +649,7 @@ def _process_db_access(
642
649
  job = oc.get(
643
650
  namespace_name,
644
651
  "Job",
645
- f"dbam-{db_access.name}",
652
+ resource_prefix,
646
653
  allow_not_found=True,
647
654
  )
648
655
  if not job:
@@ -652,8 +659,8 @@ def _process_db_access(
652
659
  oc_map=oc_map,
653
660
  cluster=cluster_name,
654
661
  namespace=namespace_name,
655
- resource_type=r.resource.kind,
656
- resource=r.resource,
662
+ resource_type=r.kind,
663
+ resource=r,
657
664
  wait_for_namespace=False,
658
665
  )
659
666
  return
@@ -665,34 +672,31 @@ def _process_db_access(
665
672
  )
666
673
  if job_status.is_complete():
667
674
  if job_status.has_errors():
668
- raise JobFailedError(
669
- f"Job dbam-{db_access.name} failed, please check logs"
670
- )
675
+ raise JobFailedError(f"Job {resource_prefix} failed, please check logs")
671
676
  if not dry_run and not db_access.delete:
672
677
  secret = {
673
- "path": f"{vault_output_path}/{QONTRACT_INTEGRATION}/{cluster_name}/{namespace_name}/{db_access.name}",
674
- "data": connections["user"].dict(by_alias=True),
678
+ "path": f"{vault_output_path}/{QONTRACT_INTEGRATION}/{state_key}",
679
+ "data": connections["user"].model_dump(by_alias=True),
675
680
  }
676
681
  vault_client.write(secret, decode_base64=False)
677
682
  logging.debug("job completed, cleaning up")
678
683
  for r in managed_resources:
679
- if r.clean_up:
680
- openshift_base.delete(
681
- dry_run=dry_run,
682
- oc_map=oc_map,
683
- cluster=cluster_name,
684
- namespace=namespace_name,
685
- resource_type=r.resource.kind,
686
- name=r.resource.name,
687
- enable_deletion=True,
688
- )
684
+ openshift_base.delete(
685
+ dry_run=dry_run,
686
+ oc_map=oc_map,
687
+ cluster=cluster_name,
688
+ namespace=namespace_name,
689
+ resource_type=r.kind,
690
+ name=r.name,
691
+ enable_deletion=True,
692
+ )
689
693
  state.add(
690
- db_access.name,
691
- value=db_access.dict(by_alias=True),
694
+ state_key,
695
+ value=db_access.model_dump(by_alias=True),
692
696
  force=True,
693
697
  )
694
698
  else:
695
- logging.info(f"Job dbam-{db_access.name} appears to be still running")
699
+ logging.info(f"Job {resource_prefix} appears to be still running")
696
700
 
697
701
 
698
702
  class DBAMIntegrationParams(PydanticRunParams):
@@ -734,9 +738,11 @@ class DatabaseAccessManagerIntegration(QontractReconcileIntegration):
734
738
 
735
739
  for db_access in resource.database_access or []:
736
740
  try:
741
+ state_key = f"{external_resource.provisioner.name}/{resource.identifier}/{db_access.name}"
737
742
  _process_db_access(
738
743
  dry_run,
739
744
  state,
745
+ state_key,
740
746
  db_access,
741
747
  namespace,
742
748
  admin_secret_name,
@@ -82,7 +82,7 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
82
82
  return {
83
83
  "version": QONTRACT_INTEGRATION_VERSION,
84
84
  "specs": {
85
- spec.name: spec.dict()
85
+ spec.name: spec.model_dump()
86
86
  for spec in get_dynatrace_token_provider_token_specs()
87
87
  },
88
88
  }
@@ -150,7 +150,10 @@ class EndpointsDiscoveryIntegration(
150
150
  return []
151
151
 
152
152
  routes = defaultdict(list)
153
- for item in oc.get_items(kind="Route", namespace=namespace.name):
153
+ for item in oc.get_items(
154
+ kind="Route.route.openshift.io",
155
+ namespace=namespace.name,
156
+ ):
154
157
  tls = bool(item["spec"].get("tls"))
155
158
  host = item["spec"]["host"]
156
159
  # group all routes with the same hostname/tls
@@ -89,7 +89,7 @@ class Renderer:
89
89
 
90
90
  def render_description(self, hash: str) -> str:
91
91
  """Render the description for a merge request."""
92
- return DESC.safe_substitute(EPDInfo(hash=hash).dict())
92
+ return DESC.safe_substitute(EPDInfo(hash=hash).model_dump())
93
93
 
94
94
  def render_title(self) -> str:
95
95
  """Render the title for a merge request."""
@@ -1,8 +1,7 @@
1
1
  import hashlib
2
- import json
3
2
  import logging
4
3
  from collections.abc import Sequence
5
- from typing import Any, TypeAlias
4
+ from typing import Any
6
5
 
7
6
  from gitlab.exceptions import GitlabGetError
8
7
  from pydantic import BaseModel
@@ -16,6 +15,7 @@ from reconcile.endpoints_discovery.merge_request import (
16
15
  Renderer,
17
16
  )
18
17
  from reconcile.utils.gitlab_api import GitLabApi
18
+ from reconcile.utils.json import json_dumps
19
19
  from reconcile.utils.merge_request_manager.merge_request_manager import (
20
20
  MergeRequestManagerBase,
21
21
  )
@@ -64,22 +64,20 @@ class Endpoint(BaseModel):
64
64
 
65
65
  @property
66
66
  def hash(self) -> str:
67
- return hashlib.sha256(
68
- json.dumps(self.dict(), sort_keys=True).encode()
69
- ).hexdigest()
67
+ return hashlib.sha256(json_dumps(self.model_dump()).encode()).hexdigest()
70
68
 
71
69
 
72
- EndpointsToAdd: TypeAlias = list[Endpoint]
73
- EndpointsToChange: TypeAlias = list[Endpoint]
74
- EndpointsToDelete: TypeAlias = list[Endpoint]
70
+ EndpointsToAdd = list[Endpoint]
71
+ EndpointsToChange = list[Endpoint]
72
+ EndpointsToDelete = list[Endpoint]
75
73
 
76
74
 
77
75
  class App(BaseModel):
78
76
  name: str
79
77
  path: str
80
- endpoints_to_add: EndpointsToAdd = EndpointsToAdd()
81
- endpoints_to_change: EndpointsToChange = EndpointsToChange()
82
- endpoints_to_delete: EndpointsToDelete = EndpointsToDelete()
78
+ endpoints_to_add: EndpointsToAdd = []
79
+ endpoints_to_change: EndpointsToChange = []
80
+ endpoints_to_delete: EndpointsToDelete = []
83
81
 
84
82
  @property
85
83
  def hash(self) -> str:
@@ -2,7 +2,7 @@ from abc import (
2
2
  ABC,
3
3
  abstractmethod,
4
4
  )
5
- from typing import Generic, TypeVar
5
+ from typing import TypeVar
6
6
 
7
7
  from reconcile.external_resources.aws import (
8
8
  AWSDefaultResourceFactory,
@@ -32,16 +32,8 @@ from reconcile.utils.secret_reader import SecretReaderBase
32
32
 
33
33
  T = TypeVar("T")
34
34
 
35
- AWS_DEFAULT_TAGS = [
36
- {
37
- "tags": {
38
- "app": "app-sre-infra",
39
- }
40
- }
41
- ]
42
35
 
43
-
44
- class ObjectFactory(Generic[T]):
36
+ class ObjectFactory[T]:
45
37
  def __init__(
46
38
  self, factories: dict[str, T], default_factory: T | None = None
47
39
  ) -> None:
@@ -123,12 +115,14 @@ class AWSExternalResourceFactory(ExternalResourceFactory):
123
115
  secret_reader: SecretReaderBase,
124
116
  provision_factories: ObjectFactory[ModuleProvisionDataFactory],
125
117
  resource_factories: ObjectFactory[AWSResourceFactory],
118
+ default_tags: dict[str, str],
126
119
  ):
127
120
  self.provision_factories = provision_factories
128
121
  self.resource_factories = resource_factories
129
122
  self.module_inventory = module_inventory
130
123
  self.er_inventory = er_inventory
131
124
  self.secret_reader = secret_reader
125
+ self.default_tags = default_tags
132
126
 
133
127
  def create_external_resource(
134
128
  self,
@@ -137,8 +131,7 @@ class AWSExternalResourceFactory(ExternalResourceFactory):
137
131
  ) -> ExternalResource:
138
132
  f = self.resource_factories.get_factory(spec.provider)
139
133
  data = f.resolve(spec, module_conf)
140
- data["tags"] = spec.tags(integration=QONTRACT_INTEGRATION)
141
- data["default_tags"] = AWS_DEFAULT_TAGS
134
+ data["tags"] = self.default_tags | spec.tags(integration=QONTRACT_INTEGRATION)
142
135
 
143
136
  region = data.get("region")
144
137
  if region:
@@ -45,7 +45,7 @@ from reconcile.utils.secret_reader import SecretReaderBase, create_secret_reader
45
45
  def fetch_current_state(
46
46
  ri: ResourceInventory, oc: OCCli, cluster: str, namespace: str
47
47
  ) -> None:
48
- for item in oc.get_items("Job", namespace=namespace):
48
+ for item in oc.get_items("Job.batch", namespace=namespace):
49
49
  r = OpenshiftResource(item, QONTRACT_INTEGRATION, QONTRACT_INTEGRATION_VERSION)
50
50
  ri.add_current(cluster, namespace, "Job", r.name, r)
51
51
 
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from collections import Counter
3
3
  from collections.abc import Iterable
4
- from datetime import UTC, datetime
4
+ from typing import cast
5
5
 
6
6
  from sretoolbox.utils import threaded
7
7
 
@@ -41,9 +41,11 @@ from reconcile.external_resources.state import (
41
41
  from reconcile.gql_definitions.external_resources.external_resources_settings import (
42
42
  ExternalResourcesSettingsV1,
43
43
  )
44
+ from reconcile.utils.datetime_util import utc_now
44
45
  from reconcile.utils.external_resource_spec import (
45
46
  ExternalResourceSpec,
46
47
  )
48
+ from reconcile.utils.json import json_dumps
47
49
  from reconcile.utils.secret_reader import SecretReaderBase
48
50
 
49
51
 
@@ -67,6 +69,7 @@ def setup_factories(
67
69
  resource_factories=setup_aws_resource_factories(
68
70
  er_inventory, secret_reader
69
71
  ),
72
+ default_tags=cast("dict[str, str]", settings.default_tags),
70
73
  )
71
74
  }
72
75
  )
@@ -141,7 +144,7 @@ class ExternalResourcesManager:
141
144
  def _resource_drift_detection_ttl_expired(
142
145
  self, reconciliation: Reconciliation, state: ExternalResourceState
143
146
  ) -> bool:
144
- return (datetime.now(state.ts.tzinfo) - state.ts).total_seconds() > (
147
+ return (utc_now() - state.ts).total_seconds() > (
145
148
  reconciliation.module_configuration.reconcile_drift_interval_minutes * 60
146
149
  )
147
150
 
@@ -242,7 +245,7 @@ class ExternalResourcesManager:
242
245
  reconciliation = Reconciliation(
243
246
  key=key,
244
247
  resource_hash=resource.hash(),
245
- input=resource.json(),
248
+ input=json_dumps(resource),
246
249
  action=Action.APPLY,
247
250
  module_configuration=module_conf,
248
251
  linked_resources=self._find_linked_resources(spec),
@@ -250,11 +253,15 @@ class ExternalResourcesManager:
250
253
  r.add(reconciliation)
251
254
  return r
252
255
 
253
- def _get_deleted_objects_reconciliations(self) -> set[Reconciliation]:
256
+ def _get_deleted_objects_reconciliations(
257
+ self, enable_migration: bool = False
258
+ ) -> set[Reconciliation]:
254
259
  to_reconcile: set[Reconciliation] = set()
255
260
  deleted_keys = (k for k, v in self.er_inventory.items() if v.marked_to_delete)
256
261
  for key in deleted_keys:
257
- state = self.state_mgr.get_external_resource_state(key)
262
+ state = self.state_mgr.get_external_resource_state(
263
+ key, enable_migration=enable_migration
264
+ )
258
265
  if state.resource_status == ResourceStatus.NOT_EXISTS:
259
266
  logging.debug("Resource has already been removed. key: %s", key)
260
267
  continue
@@ -347,7 +354,9 @@ class ExternalResourcesManager:
347
354
 
348
355
  if r.linked_resources:
349
356
  for lr in r.linked_resources:
350
- lrs = self.state_mgr.get_external_resource_state(lr)
357
+ lrs = self.state_mgr.get_external_resource_state(
358
+ lr, enable_migration=True
359
+ )
351
360
  if not lrs.resource_status.is_in_progress:
352
361
  lrs.resource_status = ResourceStatus.RECONCILIATION_REQUESTED
353
362
  self.state_mgr.set_external_resource_state(lrs)
@@ -355,7 +364,7 @@ class ExternalResourcesManager:
355
364
  def _set_resource_reconciliation_in_progress(
356
365
  self, r: Reconciliation, state: ExternalResourceState
357
366
  ) -> None:
358
- state.ts = datetime.now(UTC)
367
+ state.ts = utc_now()
359
368
  if r.action == Action.APPLY:
360
369
  state.resource_status = ResourceStatus.IN_PROGRESS
361
370
  elif r.action == Action.DESTROY:
@@ -414,10 +423,12 @@ class ExternalResourcesManager:
414
423
 
415
424
  def handle_resources(self) -> None:
416
425
  desired_r = self._get_desired_objects_reconciliations()
417
- deleted_r = self._get_deleted_objects_reconciliations()
426
+ deleted_r = self._get_deleted_objects_reconciliations(enable_migration=True)
418
427
  to_sync_keys: set[ExternalResourceKey] = set()
419
428
  for r in desired_r.union(deleted_r):
420
- state = self.state_mgr.get_external_resource_state(r.key)
429
+ state = self.state_mgr.get_external_resource_state(
430
+ r.key, enable_migration=True
431
+ )
421
432
  reconciliation_status = self._get_reconciliation_status(r, state)
422
433
  self._update_resource_state(r, state, reconciliation_status)
423
434
 
@@ -448,7 +459,10 @@ class ExternalResourcesManager:
448
459
  r
449
460
  for r in desired_r.union(deleted_r)
450
461
  if self._reconciliation_needs_dry_run_run(
451
- r, self.state_mgr.get_external_resource_state(key=r.key)
462
+ r,
463
+ self.state_mgr.get_external_resource_state(
464
+ key=r.key, enable_migration=False
465
+ ),
452
466
  )
453
467
  }
454
468
 
@@ -10,7 +10,6 @@ SECRET_ANN_PROVISIONER = SECRET_ANN_PREFIX + "/provisioner_name"
10
10
  SECRET_ANN_PROVIDER = SECRET_ANN_PREFIX + "/provider"
11
11
  SECRET_ANN_IDENTIFIER = SECRET_ANN_PREFIX + "/identifier"
12
12
  SECRET_UPDATED_AT = SECRET_ANN_PREFIX + "/updated_at"
13
- SECRET_UPDATED_AT_TIMEFORMAT = "%Y-%m-%dT%H:%M:%SZ"
14
13
 
15
14
  FLAG_RESOURCE_MANAGED_BY_ERV2 = "managed_by_erv2"
16
15
  FLAG_DELETE_RESOURCE = "delete"
@@ -13,7 +13,7 @@ from reconcile.utils.metrics import (
13
13
 
14
14
 
15
15
  class ExternalResourcesBaseMetric(BaseModel):
16
- integration = normalize_integration_name(QONTRACT_INTEGRATION)
16
+ integration: str = normalize_integration_name(QONTRACT_INTEGRATION)
17
17
  app: str
18
18
  environment: str
19
19
  provision_provider: str