qontract-reconcile 0.10.2.dev394__py3-none-any.whl → 0.10.2.dev427__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 (316) hide show
  1. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev427.dist-info}/METADATA +5 -4
  2. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev427.dist-info}/RECORD +316 -315
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +18 -12
  5. reconcile/aus/base.py +117 -18
  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 +6 -6
  14. reconcile/aws_account_manager/reconciler.py +3 -3
  15. reconcile/aws_ami_cleanup/integration.py +2 -5
  16. reconcile/aws_ami_share.py +69 -62
  17. reconcile/aws_saml_idp/integration.py +5 -3
  18. reconcile/aws_saml_roles/integration.py +23 -22
  19. reconcile/aws_version_sync/integration.py +6 -12
  20. reconcile/change_owners/bundle.py +3 -3
  21. reconcile/change_owners/change_log_tracking.py +3 -2
  22. reconcile/change_owners/change_owners.py +1 -1
  23. reconcile/cli.py +62 -4
  24. reconcile/dashdotdb_dora.py +1 -1
  25. reconcile/dashdotdb_slo.py +1 -1
  26. reconcile/database_access_manager.py +8 -9
  27. reconcile/dynatrace_token_provider/integration.py +1 -1
  28. reconcile/endpoints_discovery/integration.py +4 -1
  29. reconcile/endpoints_discovery/merge_request.py +1 -1
  30. reconcile/endpoints_discovery/merge_request_manager.py +1 -1
  31. reconcile/external_resources/integration.py +1 -1
  32. reconcile/external_resources/manager.py +3 -2
  33. reconcile/external_resources/metrics.py +1 -1
  34. reconcile/external_resources/model.py +13 -13
  35. reconcile/external_resources/reconciler.py +7 -4
  36. reconcile/external_resources/secrets_sync.py +2 -2
  37. reconcile/external_resources/state.py +22 -13
  38. reconcile/fleet_labeler/integration.py +1 -1
  39. reconcile/gcp_image_mirror.py +2 -2
  40. reconcile/github_org.py +1 -1
  41. reconcile/github_owners.py +4 -0
  42. reconcile/gitlab_members.py +6 -12
  43. reconcile/gitlab_permissions.py +8 -12
  44. reconcile/glitchtip_project_alerts/integration.py +3 -1
  45. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  46. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  47. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  48. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  49. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  50. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  51. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  52. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  53. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  54. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
  55. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +5 -5
  56. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +5 -5
  57. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +5 -5
  58. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +5 -5
  59. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  60. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  61. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  62. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  63. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  64. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  65. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  66. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  67. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  68. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  69. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  70. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  71. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  72. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  73. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  74. reconcile/gql_definitions/common/apps.py +5 -5
  75. reconcile/gql_definitions/common/aws_vpc_requests.py +5 -5
  76. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  77. reconcile/gql_definitions/common/clusters.py +5 -5
  78. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  79. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  80. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  81. reconcile/gql_definitions/common/github_orgs.py +5 -5
  82. reconcile/gql_definitions/common/jira_settings.py +5 -5
  83. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  84. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  85. reconcile/gql_definitions/common/namespaces.py +5 -5
  86. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  87. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  88. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  89. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  90. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  91. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  92. reconcile/gql_definitions/common/quay_instances.py +5 -5
  93. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  94. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  95. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  96. reconcile/gql_definitions/common/saas_files.py +5 -5
  97. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  98. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  99. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  100. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  101. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  102. reconcile/gql_definitions/common/users.py +5 -5
  103. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  104. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  105. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  106. reconcile/gql_definitions/cost_report/settings.py +5 -5
  107. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  108. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  109. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  110. reconcile/gql_definitions/email_sender/apps.py +5 -5
  111. reconcile/gql_definitions/email_sender/emails.py +5 -5
  112. reconcile/gql_definitions/email_sender/users.py +5 -5
  113. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  114. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  115. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  116. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +5 -5
  117. reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
  118. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  119. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  120. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  121. reconcile/gql_definitions/fragments/aws_account_common.py +5 -5
  122. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  123. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  124. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  125. reconcile/gql_definitions/fragments/aws_organization.py +5 -5
  126. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  127. reconcile/gql_definitions/fragments/aws_vpc_request.py +5 -5
  128. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  129. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  130. reconcile/gql_definitions/fragments/disable.py +5 -5
  131. reconcile/gql_definitions/fragments/email_service.py +5 -5
  132. reconcile/gql_definitions/fragments/email_user.py +5 -5
  133. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  134. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  135. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  136. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  137. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  138. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  139. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  140. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  141. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  142. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  143. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  144. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  145. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  146. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  147. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  148. reconcile/gql_definitions/fragments/user.py +5 -5
  149. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  150. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  151. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  152. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  153. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  154. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  155. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  156. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  157. reconcile/gql_definitions/integrations/integrations.py +5 -5
  158. reconcile/gql_definitions/introspection.json +231 -0
  159. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  160. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  161. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  162. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +5 -5
  163. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  164. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  165. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  166. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  167. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  168. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  169. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  170. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  171. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  172. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  173. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  174. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  175. reconcile/gql_definitions/rhcs/certs.py +24 -79
  176. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +42 -0
  177. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  178. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  179. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  180. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  181. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  182. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  183. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  184. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  185. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  186. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  187. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  188. reconcile/gql_definitions/status_board/status_board.py +5 -5
  189. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  190. reconcile/gql_definitions/templating/template_collection.py +5 -5
  191. reconcile/gql_definitions/templating/templates.py +5 -5
  192. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  193. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  194. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  195. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  196. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  197. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  198. reconcile/gql_definitions/terraform_init/aws_accounts.py +5 -5
  199. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  200. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  201. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +5 -5
  202. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +5 -5
  203. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  204. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  205. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  206. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
  207. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  208. reconcile/integrations_manager.py +3 -3
  209. reconcile/jenkins_worker_fleets.py +9 -8
  210. reconcile/jira_permissions_validator.py +2 -2
  211. reconcile/ldap_groups/integration.py +1 -1
  212. reconcile/ocm/types.py +35 -57
  213. reconcile/ocm_aws_infrastructure_access.py +1 -1
  214. reconcile/ocm_clusters.py +4 -4
  215. reconcile/ocm_labels/integration.py +3 -2
  216. reconcile/ocm_machine_pools.py +33 -27
  217. reconcile/openshift_base.py +113 -4
  218. reconcile/openshift_cluster_bots.py +1 -1
  219. reconcile/openshift_namespace_labels.py +1 -1
  220. reconcile/openshift_namespaces.py +97 -101
  221. reconcile/openshift_resources_base.py +6 -2
  222. reconcile/openshift_rhcs_certs.py +27 -29
  223. reconcile/openshift_rolebindings.py +7 -11
  224. reconcile/openshift_saas_deploy.py +4 -5
  225. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  226. reconcile/openshift_serviceaccount_tokens.py +2 -2
  227. reconcile/openshift_upgrade_watcher.py +1 -1
  228. reconcile/oum/labelset.py +5 -3
  229. reconcile/oum/models.py +1 -4
  230. reconcile/prometheus_rules_tester/integration.py +3 -3
  231. reconcile/quay_mirror.py +1 -1
  232. reconcile/queries.py +6 -0
  233. reconcile/rhidp/common.py +3 -5
  234. reconcile/rhidp/sso_client/base.py +16 -5
  235. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  236. reconcile/skupper_network/integration.py +2 -2
  237. reconcile/slack_usergroups.py +31 -11
  238. reconcile/status_board.py +6 -6
  239. reconcile/statuspage/atlassian.py +7 -7
  240. reconcile/statuspage/page.py +4 -9
  241. reconcile/templating/lib/rendering.py +3 -3
  242. reconcile/templating/renderer.py +2 -2
  243. reconcile/terraform_cloudflare_dns.py +3 -3
  244. reconcile/terraform_cloudflare_resources.py +5 -5
  245. reconcile/terraform_cloudflare_users.py +3 -2
  246. reconcile/terraform_init/integration.py +2 -2
  247. reconcile/terraform_repo.py +16 -12
  248. reconcile/terraform_resources.py +6 -6
  249. reconcile/terraform_tgw_attachments.py +20 -18
  250. reconcile/terraform_vpc_resources/integration.py +3 -1
  251. reconcile/typed_queries/cost_report/app_names.py +1 -1
  252. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  253. reconcile/typed_queries/saas_files.py +11 -11
  254. reconcile/typed_queries/status_board.py +2 -2
  255. reconcile/unleash_feature_toggles/integration.py +4 -2
  256. reconcile/utils/acs/base.py +6 -3
  257. reconcile/utils/acs/policies.py +2 -2
  258. reconcile/utils/aws_api.py +51 -20
  259. reconcile/utils/aws_api_typed/organization.py +4 -2
  260. reconcile/utils/binary.py +7 -12
  261. reconcile/utils/deadmanssnitch_api.py +1 -1
  262. reconcile/utils/early_exit_cache.py +8 -10
  263. reconcile/utils/gitlab_api.py +7 -5
  264. reconcile/utils/glitchtip/client.py +6 -2
  265. reconcile/utils/glitchtip/models.py +25 -28
  266. reconcile/utils/gql.py +4 -7
  267. reconcile/utils/instrumented_wrappers.py +1 -1
  268. reconcile/utils/internal_groups/client.py +2 -2
  269. reconcile/utils/internal_groups/models.py +8 -17
  270. reconcile/utils/jinja2/utils.py +2 -5
  271. reconcile/utils/jobcontroller/controller.py +2 -2
  272. reconcile/utils/jobcontroller/models.py +17 -1
  273. reconcile/utils/json.py +43 -1
  274. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  275. reconcile/utils/membershipsources/models.py +16 -23
  276. reconcile/utils/membershipsources/resolver.py +4 -2
  277. reconcile/utils/merge_request_manager/merge_request_manager.py +1 -1
  278. reconcile/utils/merge_request_manager/parser.py +4 -4
  279. reconcile/utils/metrics.py +5 -5
  280. reconcile/utils/models.py +304 -82
  281. reconcile/utils/mr/notificator.py +1 -1
  282. reconcile/utils/mr/user_maintenance.py +3 -2
  283. reconcile/utils/oc.py +246 -201
  284. reconcile/utils/ocm/addons.py +0 -1
  285. reconcile/utils/ocm/base.py +17 -20
  286. reconcile/utils/ocm/cluster_groups.py +1 -1
  287. reconcile/utils/ocm/identity_providers.py +2 -2
  288. reconcile/utils/ocm/labels.py +1 -1
  289. reconcile/utils/ocm/products.py +8 -8
  290. reconcile/utils/ocm/service_log.py +1 -1
  291. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  292. reconcile/utils/openshift_resource.py +5 -0
  293. reconcile/utils/pagerduty_api.py +5 -2
  294. reconcile/utils/promotion_state.py +6 -11
  295. reconcile/utils/raw_github_api.py +1 -1
  296. reconcile/utils/rhcsv2_certs.py +1 -4
  297. reconcile/utils/rosa/session.py +16 -0
  298. reconcile/utils/runtime/integration.py +1 -1
  299. reconcile/utils/saasherder/interfaces.py +13 -20
  300. reconcile/utils/saasherder/models.py +23 -20
  301. reconcile/utils/saasherder/saasherder.py +46 -24
  302. reconcile/utils/slack_api.py +2 -2
  303. reconcile/utils/structs.py +1 -1
  304. reconcile/utils/terraform_client.py +1 -1
  305. reconcile/utils/terrascript_aws_client.py +47 -43
  306. reconcile/utils/unleash/server.py +2 -8
  307. reconcile/utils/vault.py +5 -12
  308. reconcile/utils/vcs.py +8 -8
  309. reconcile/vault_replication.py +1 -1
  310. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  311. tools/cli_commands/cost_report/view.py +7 -6
  312. tools/cli_commands/erv2.py +1 -1
  313. tools/qontract_cli.py +6 -5
  314. tools/template_validation.py +3 -1
  315. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev427.dist-info}/WHEEL +0 -0
  316. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev427.dist-info}/entry_points.txt +0 -0
reconcile/quay_mirror.py CHANGED
@@ -66,7 +66,7 @@ class QuayMirror:
66
66
  }
67
67
  """
68
68
 
69
- response_cache: dict[tuple[str, str], Response] = {}
69
+ response_cache: dict[tuple[str, str | None], Response] = {}
70
70
 
71
71
  def __init__(
72
72
  self,
reconcile/queries.py CHANGED
@@ -505,6 +505,12 @@ AWS_ACCOUNTS_QUERY = """
505
505
  name
506
506
  uid
507
507
  supportedDeploymentRegions
508
+ organization {
509
+ payerAccount {
510
+ organizationAccountTags
511
+ }
512
+ tags
513
+ }
508
514
  }
509
515
  ... on AWSAccountSharingOptionAMI_v1 {
510
516
  regex
reconcile/rhidp/common.py CHANGED
@@ -7,10 +7,7 @@ from enum import StrEnum
7
7
  from typing import Any
8
8
  from urllib.parse import urlparse
9
9
 
10
- from pydantic import (
11
- BaseModel,
12
- root_validator,
13
- )
10
+ from pydantic import BaseModel, model_validator
14
11
 
15
12
  from reconcile.gql_definitions.common.ocm_environments import (
16
13
  query as ocm_environment_query,
@@ -63,7 +60,8 @@ class ClusterAuth(BaseModel):
63
60
  issuer: str
64
61
  status: str
65
62
 
66
- @root_validator
63
+ @model_validator(mode="before")
64
+ @classmethod
67
65
  def name_no_spaces(
68
66
  cls, values: MutableMapping[str, Any]
69
67
  ) -> MutableMapping[str, Any]:
@@ -1,3 +1,4 @@
1
+ import http
1
2
  import logging
2
3
  from collections.abc import (
3
4
  Iterable,
@@ -10,6 +11,7 @@ from urllib.parse import (
10
11
  )
11
12
 
12
13
  import jwt
14
+ from requests import HTTPError
13
15
 
14
16
  from reconcile.rhidp.common import (
15
17
  Cluster,
@@ -237,7 +239,7 @@ def create_sso_client(
237
239
  secret_reader.vault_client.write(
238
240
  secret={
239
241
  "path": secret.path,
240
- "data": sso_client.dict(),
242
+ "data": sso_client.model_dump(),
241
243
  },
242
244
  decode_base64=False,
243
245
  )
@@ -256,9 +258,18 @@ def delete_sso_client(
256
258
  )
257
259
  sso_client = SSOClient(**secret_reader.read_all_secret(secret=secret))
258
260
  keycloak_api = keycloak_map.get(sso_client.issuer)
259
- keycloak_api.delete_client(
260
- registration_client_uri=sso_client.registration_client_uri,
261
- registration_access_token=sso_client.registration_access_token,
262
- )
261
+ try:
262
+ keycloak_api.delete_client(
263
+ registration_client_uri=sso_client.registration_client_uri,
264
+ registration_access_token=sso_client.registration_access_token,
265
+ )
266
+ except HTTPError as e:
267
+ if e.response.status_code != http.HTTPStatus.UNAUTHORIZED:
268
+ logging.error(f"Failed to delete SSO client {sso_client_id}: {e}")
269
+ raise
270
+ # something went wrong with the registration token, maybe it expired
271
+ logging.error(
272
+ f"Failed to delete SSO client {sso_client_id} due to unauthorized error: {e}. Continuing to delete the vault secret."
273
+ )
263
274
 
264
275
  secret_reader.vault_client.delete(path=secret.path)
@@ -177,7 +177,7 @@ def is_namespace_addressed_by_selector(
177
177
  # json representation of namespace to filter on
178
178
  # remove all the None values to simplify the jsonpath expressions
179
179
  namespace_as_dict = {
180
- "namespace": [namespace.dict(by_alias=True, exclude_none=True)]
180
+ "namespace": [namespace.model_dump(by_alias=True, exclude_none=True)]
181
181
  }
182
182
 
183
183
  do_include = any(
@@ -85,7 +85,7 @@ def compile_skupper_sites(
85
85
  or skupper_network.site_controller_templates
86
86
  ):
87
87
  tmpl_vars = tmpl.variables or {}
88
- tmpl_vars["resource"] = {"namespace": ns.dict(by_alias=True)}
88
+ tmpl_vars["resource"] = {"namespace": ns.model_dump(by_alias=True)}
89
89
 
90
90
  site_controller_objects.append(
91
91
  load_site_controller_template(tmpl.path, tmpl_vars)
@@ -304,6 +304,6 @@ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
304
304
  skupper_networks = get_skupper_networks(gqlapi.query)
305
305
  return {
306
306
  "skupper_sites": [
307
- site.dict() for site in compile_skupper_sites(skupper_networks)
307
+ site.model_dump() for site in compile_skupper_sites(skupper_networks)
308
308
  ],
309
309
  }
@@ -3,18 +3,19 @@ import sys
3
3
  from collections.abc import (
4
4
  Callable,
5
5
  Iterable,
6
+ MutableMapping,
6
7
  Sequence,
7
8
  )
8
9
  from datetime import datetime
9
10
  from typing import (
10
11
  Any,
11
12
  TypedDict,
13
+ TypeVar,
12
14
  )
13
15
 
14
16
  from github.GithubException import UnknownObjectException
15
17
  from pydantic import BaseModel
16
- from pydantic.utils import deep_update
17
- from sretoolbox.utils import retry
18
+ from sretoolbox.utils import datatransformation, retry
18
19
 
19
20
  from reconcile import (
20
21
  openshift_users,
@@ -75,6 +76,26 @@ INTEGRATION_VERSION = "0.1.0"
75
76
 
76
77
  error_occurred = False
77
78
 
79
+ KeyType = TypeVar("KeyType")
80
+
81
+
82
+ def deep_update(
83
+ mapping: dict[KeyType, Any],
84
+ *updating_mappings: MutableMapping[KeyType, Any],
85
+ ) -> dict[KeyType, Any]:
86
+ updated_mapping = mapping.copy()
87
+ for updating_mapping in updating_mappings:
88
+ for k, v in updating_mapping.items():
89
+ if (
90
+ k in updated_mapping
91
+ and isinstance(updated_mapping[k], dict)
92
+ and isinstance(v, dict)
93
+ ):
94
+ updated_mapping[k] = deep_update(updated_mapping[k], v)
95
+ else:
96
+ updated_mapping[k] = v
97
+ return updated_mapping
98
+
78
99
 
79
100
  def get_git_api(url: str) -> GithubRepositoryApi | GitLabApi:
80
101
  """Return GitHub/GitLab API based on url."""
@@ -123,15 +144,12 @@ class State(BaseModel):
123
144
  SlackState = dict[str, dict[str, State]]
124
145
 
125
146
 
126
- class WorkspaceSpec(BaseModel):
147
+ class WorkspaceSpec(BaseModel, arbitrary_types_allowed=True):
127
148
  """Slack workspace spec."""
128
149
 
129
150
  slack: SlackApi
130
151
  managed_usergroups: list[str] = []
131
152
 
132
- class Config:
133
- arbitrary_types_allowed = True
134
-
135
153
 
136
154
  SlackMap = dict[str, WorkspaceSpec]
137
155
 
@@ -820,7 +838,9 @@ def run(
820
838
  desired_usergroup_name=usergroup_name,
821
839
  )
822
840
  # merge the two desired states recursively
823
- desired_state = deep_update(desired_state, desired_state_cluster_usergroups)
841
+ desired_state = datatransformation.deep_merge(
842
+ desired_state, desired_state_cluster_usergroups
843
+ )
824
844
 
825
845
  runner_params: RunnerParams = {
826
846
  "dry_run": dry_run,
@@ -892,10 +912,10 @@ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
892
912
  if role.tag_on_cluster_updates is not False
893
913
  ]
894
914
  return {
895
- "permissions": [p.dict() for p in get_permissions(gqlapi.query)],
915
+ "permissions": [p.model_dump() for p in get_permissions(gqlapi.query)],
896
916
  "pagerduty_instances": [
897
- p.dict() for p in get_pagerduty_instances(gqlapi.query)
917
+ p.model_dump() for p in get_pagerduty_instances(gqlapi.query)
898
918
  ],
899
- "users": [u.dict() for u in users],
900
- "clusters": [c.dict() for c in get_clusters(gqlapi.query)],
919
+ "users": [u.model_dump() for u in users],
920
+ "clusters": [c.model_dump() for c in get_clusters(gqlapi.query)],
901
921
  }
reconcile/status_board.py CHANGED
@@ -52,7 +52,7 @@ class AbstractStatusBoard(ABC, BaseModel):
52
52
  """Abstract class for upgrade policies
53
53
  Used to create and delete upgrade policies in OCM."""
54
54
 
55
- id: str | None
55
+ id: str | None = None
56
56
  name: str
57
57
  fullname: str
58
58
 
@@ -88,7 +88,7 @@ class AbstractStatusBoard(ABC, BaseModel):
88
88
 
89
89
 
90
90
  class Product(AbstractStatusBoard):
91
- applications: list["Application"] | None
91
+ applications: list["Application"] | None = None
92
92
 
93
93
  def create(self, ocm: OCMBaseClient) -> None:
94
94
  spec = self.to_ocm_spec()
@@ -121,7 +121,7 @@ class Product(AbstractStatusBoard):
121
121
 
122
122
  class Application(AbstractStatusBoard):
123
123
  product: Product
124
- services: list["Service"] | None
124
+ services: list["Service"] | None = None
125
125
 
126
126
  def create(self, ocm: OCMBaseClient) -> None:
127
127
  if self.product.id:
@@ -214,9 +214,9 @@ class Service(AbstractStatusBoard):
214
214
 
215
215
 
216
216
  # Resolve forward references after class definitions
217
- Product.update_forward_refs()
218
- Application.update_forward_refs()
219
- Service.update_forward_refs()
217
+ Product.model_rebuild()
218
+ Application.model_rebuild()
219
+ Service.model_rebuild()
220
220
 
221
221
 
222
222
  class UpdateNotSupportedError(Exception):
@@ -29,12 +29,12 @@ class AtlassianRawComponent(BaseModel):
29
29
 
30
30
  id: str
31
31
  name: str
32
- description: str | None
32
+ description: str | None = None
33
33
  position: int
34
34
  status: str
35
- automation_email: str | None
36
- group_id: str | None
37
- group: bool | None
35
+ automation_email: str | None = None
36
+ group_id: str | None = None
37
+ group: bool | None = None
38
38
 
39
39
 
40
40
  class AtlassianRawMaintenanceUpdate(BaseModel):
@@ -56,9 +56,9 @@ class AtlassianRawMaintenance(BaseModel):
56
56
  scheduled_until: str
57
57
  incident_updates: list[AtlassianRawMaintenanceUpdate]
58
58
  components: list[AtlassianRawComponent]
59
- auto_transition_deliver_notifications_at_end: bool | None
60
- auto_transition_deliver_notifications_at_start: bool | None
61
- scheduled_remind_prior: bool | None
59
+ auto_transition_deliver_notifications_at_end: bool | None = None
60
+ auto_transition_deliver_notifications_at_start: bool | None = None
61
+ scheduled_remind_prior: bool | None = None
62
62
 
63
63
 
64
64
  class AtlassianAPI:
@@ -19,19 +19,17 @@ from reconcile.statuspage.status import (
19
19
  PROVIDER_NAME = "statuspage"
20
20
 
21
21
 
22
- class StatusComponent(BaseModel):
22
+ class StatusComponent(BaseModel, arbitrary_types_allowed=True):
23
23
  """
24
24
  Represents a status page component from the desired state.
25
25
  """
26
26
 
27
27
  name: str
28
28
  display_name: str
29
- description: str | None
30
- group_name: str | None
29
+ description: str | None = None
30
+ group_name: str | None = None
31
+ # Status provider configs hold different ways for a component to determine its status
31
32
  status_provider_configs: list[StatusProvider]
32
- """
33
- Status provider configs hold different ways for a component to determine its status
34
- """
35
33
 
36
34
  def status_management_enabled(self) -> bool:
37
35
  """
@@ -49,9 +47,6 @@ class StatusComponent(BaseModel):
49
47
  return "operational"
50
48
  return None
51
49
 
52
- class Config:
53
- arbitrary_types_allowed = True
54
-
55
50
  def __eq__(self, other: object) -> bool:
56
51
  if not isinstance(other, StatusComponent):
57
52
  raise NotImplementedError("Cannot compare to non StatusComponent objects.")
@@ -18,7 +18,7 @@ from reconcile.utils.secret_reader import SecretReaderBase
18
18
 
19
19
  class TemplateData(BaseModel):
20
20
  variables: dict[str, Any]
21
- current: dict[str, Any] | None
21
+ current: dict[str, Any] | None = None
22
22
  current_with_explicit_start: bool | None = False
23
23
 
24
24
 
@@ -26,7 +26,7 @@ class TemplatePatch(Protocol):
26
26
  path: str
27
27
  identifier: str | None
28
28
 
29
- def dict(self) -> dict[str, str]: ...
29
+ def model_dump(self) -> dict[str, str]: ...
30
30
 
31
31
 
32
32
  class Template(Protocol):
@@ -36,7 +36,7 @@ class Template(Protocol):
36
36
  template: str
37
37
  overwrite: bool | None
38
38
 
39
- def dict(self) -> dict[str, str]: ...
39
+ def model_dump(self) -> dict[str, str]: ...
40
40
 
41
41
  @property
42
42
  def patch(self) -> TemplatePatch | None:
@@ -239,8 +239,8 @@ def unpack_dynamic_variables(
239
239
 
240
240
  class TemplateRendererIntegrationParams(PydanticRunParams):
241
241
  clone_repo: bool = False
242
- app_interface_root_path: str | None
243
- template_collection_name: str | None
242
+ app_interface_root_path: str | None = None
243
+ template_collection_name: str | None = None
244
244
 
245
245
 
246
246
  def join_path(base: str, sub: str) -> str:
@@ -155,7 +155,7 @@ class TerraformCloudflareDNSIntegration(
155
155
 
156
156
  accts_per_zone = []
157
157
  for zone in query_zones.zones or []:
158
- acct = zone.account.dict(by_alias=True)
158
+ acct = zone.account.model_dump(by_alias=True)
159
159
  acct["name"] = f"{zone.account.name}-{zone.identifier}"
160
160
  accts_per_zone.append(acct)
161
161
 
@@ -369,11 +369,11 @@ def cloudflare_dns_zone_to_external_resource(
369
369
  provision_provider=DEFAULT_PROVISIONER_PROVIDER,
370
370
  provisioner={"name": f"{zone.account.name}-{zone.identifier}"},
371
371
  namespace=DEFAULT_NAMESPACE,
372
- resource=zone.dict(by_alias=True, exclude=DEFAULT_EXCLUDE_KEY),
372
+ resource=zone.model_dump(by_alias=True, exclude=DEFAULT_EXCLUDE_KEY),
373
373
  )
374
374
  external_resource_spec.resource["provider"] = DEFAULT_PROVIDER
375
375
  external_resource_spec.resource["records"] = [
376
- record.dict(by_alias=True) for record in zone.records or []
376
+ record.model_dump(by_alias=True) for record in zone.records or []
377
377
  ]
378
378
  external_resource_specs.append(external_resource_spec)
379
379
  return external_resource_specs
@@ -168,7 +168,7 @@ def _build_oc_resources(
168
168
  internal=internal,
169
169
  )
170
170
 
171
- namespace_mapping = [ns.dict() for ns in cloudflare_namespaces]
171
+ namespace_mapping = [ns.model_dump() for ns in cloudflare_namespaces]
172
172
 
173
173
  state_specs = init_specs_to_fetch(
174
174
  ri, oc_map, namespaces=namespace_mapping, override_managed_types=["Secret"]
@@ -338,7 +338,7 @@ def run(
338
338
  )
339
339
 
340
340
  if not cloudflare_namespaces:
341
- logging.info("No cloudflare namespaces were detected, nothing to do.")
341
+ logging.debug("No cloudflare namespaces were detected, nothing to do.")
342
342
  sys.exit(ExitCodes.SUCCESS)
343
343
 
344
344
  # Build Cloudflare clients
@@ -351,7 +351,7 @@ def run(
351
351
  spec
352
352
  for namespace in query_resources.namespaces
353
353
  for spec in get_external_resource_specs(
354
- namespace.dict(by_alias=True), PROVIDER_CLOUDFLARE
354
+ namespace.model_dump(by_alias=True), PROVIDER_CLOUDFLARE
355
355
  )
356
356
  if not selected_account or spec.provisioner_name == selected_account
357
357
  ]
@@ -383,7 +383,7 @@ def run(
383
383
  QONTRACT_INTEGRATION_VERSION,
384
384
  QONTRACT_TF_PREFIX,
385
385
  [
386
- acct.dict(by_alias=True) # convert CloudflareAccountV1 to dict
386
+ acct.model_dump(by_alias=True) # convert CloudflareAccountV1 to dict
387
387
  for acct in query_accounts.accounts or []
388
388
  if acct.name in cf_clients.dump() # use only if it is a registered client
389
389
  ],
@@ -442,4 +442,4 @@ def _get_cloudflare_desired_state() -> tuple[
442
442
  def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
443
443
  desired_state = _get_cloudflare_desired_state()
444
444
 
445
- return {state.__repr_name__(): state.dict() for state in desired_state}
445
+ return {str(state): state.model_dump() for state in desired_state}
@@ -88,7 +88,7 @@ class TerraformCloudflareUsers(
88
88
  if not settings.settings:
89
89
  raise RuntimeError("App interface setting not defined")
90
90
 
91
- early_exit_desired_state = cloudflare_roles.dict()
91
+ early_exit_desired_state = cloudflare_roles.model_dump()
92
92
  early_exit_desired_state.update({
93
93
  CLOUDFLARE_EMAIL_DOMAIN_ALLOW_LIST_KEY: settings.settings
94
94
  })
@@ -149,7 +149,8 @@ class TerraformCloudflareUsers(
149
149
  }
150
150
 
151
151
  accounts = [
152
- acct.dict(by_alias=True) for _, acct in account_names_to_account.items()
152
+ acct.model_dump(by_alias=True)
153
+ for _, acct in account_names_to_account.items()
153
154
  ]
154
155
 
155
156
  self._run_terraform(
@@ -34,7 +34,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
34
34
 
35
35
 
36
36
  class TerraformInitIntegrationParams(PydanticRunParams):
37
- account_name: str | None
37
+ account_name: str | None = None
38
38
  # To avoid the accidental deletion of the resource file, explicitly set the
39
39
  # qontract.cli option in the integration extraArgs!
40
40
  state_tmpl_resource: str = "/terraform-init/terraform-state.yml"
@@ -64,7 +64,7 @@ class TerraformInitIntegration(
64
64
  query_func = gql.get_api().query
65
65
  return {
66
66
  "accounts": [
67
- account.dict() for account in self.get_aws_accounts(query_func)
67
+ account.model_dump() for account in self.get_aws_accounts(query_func)
68
68
  ],
69
69
  }
70
70
 
@@ -41,10 +41,10 @@ class RepoOutput(BaseModel):
41
41
  project_path: str
42
42
  delete: bool
43
43
  aws_creds: VaultSecret
44
- variables: TerraformRepoVariablesV1 | None
45
- bucket: str | None
46
- region: str | None
47
- bucket_path: str | None
44
+ variables: TerraformRepoVariablesV1 | None = None
45
+ bucket: str | None = None
46
+ region: str | None = None
47
+ bucket_path: str | None = None
48
48
  require_fips: bool
49
49
  tf_version: str
50
50
 
@@ -62,8 +62,8 @@ class OutputFile(BaseModel):
62
62
  class TerraformRepoIntegrationParams(PydanticRunParams):
63
63
  output_file: str | None
64
64
  validate_git: bool
65
- gitlab_project_id: str | None
66
- gitlab_merge_request_id: int | None
65
+ gitlab_project_id: str | None = None
66
+ gitlab_merge_request_id: int | None = None
67
67
 
68
68
 
69
69
  class TerraformRepoIntegration(
@@ -168,7 +168,7 @@ class TerraformRepoIntegration(
168
168
  self.params.output_file, "w", encoding="locale"
169
169
  ) as output_file:
170
170
  yaml.safe_dump(
171
- data=output.dict(),
171
+ data=output.model_dump(),
172
172
  stream=output_file,
173
173
  explicit_start=True,
174
174
  )
@@ -177,7 +177,7 @@ class TerraformRepoIntegration(
177
177
  f"Unable to write to '{self.params.output_file}'"
178
178
  ) from None
179
179
  else:
180
- print(yaml.safe_dump(data=output.dict(), explicit_start=True))
180
+ print(yaml.safe_dump(data=output.model_dump(), explicit_start=True))
181
181
 
182
182
  return output
183
183
 
@@ -206,7 +206,7 @@ class TerraformRepoIntegration(
206
206
  keys = state.ls()
207
207
  for key in keys:
208
208
  if value := state.get(key.lstrip("/"), None):
209
- repo = TerraformRepoV1.parse_obj(value)
209
+ repo = TerraformRepoV1.model_validate(value)
210
210
  repo_list.append(repo)
211
211
 
212
212
  return repo_list
@@ -283,7 +283,7 @@ class TerraformRepoIntegration(
283
283
  for add_key, add_val in diff_result.add.items():
284
284
  # state.add already performs a json.dumps(key) so we export the
285
285
  # pydantic model as a dict to avoid a double json dump with extra quotes
286
- state.add(add_key, add_val.dict(by_alias=True), force=True)
286
+ state.add(add_key, add_val.model_dump(by_alias=True), force=True)
287
287
  for delete_key in diff_result.delete:
288
288
  state.rm(delete_key)
289
289
  for change_key, change_val in diff_result.change.items():
@@ -291,7 +291,9 @@ class TerraformRepoIntegration(
291
291
  state.rm(change_key)
292
292
  else:
293
293
  state.add(
294
- change_key, change_val.desired.dict(by_alias=True), force=True
294
+ change_key,
295
+ change_val.desired.model_dump(by_alias=True),
296
+ force=True,
295
297
  )
296
298
  except KeyError:
297
299
  pass
@@ -394,5 +396,7 @@ class TerraformRepoIntegration(
394
396
  def early_exit_desired_state(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
395
397
  gqlapi = gql.get_api()
396
398
  return {
397
- "repos": [repo.dict() for repo in self.get_repos(query_func=gqlapi.query)]
399
+ "repos": [
400
+ repo.model_dump() for repo in self.get_repos(query_func=gqlapi.query)
401
+ ]
398
402
  }
@@ -137,7 +137,7 @@ def fetch_current_state(
137
137
  use_jump_host=use_jump_host,
138
138
  thread_pool_size=thread_pool_size,
139
139
  )
140
- namespaces_dicts = [ns.dict(by_alias=True) for ns in namespaces]
140
+ namespaces_dicts = [ns.model_dump(by_alias=True) for ns in namespaces]
141
141
  state_specs = ob.init_specs_to_fetch(
142
142
  ri, oc_map, namespaces=namespaces_dicts, override_managed_types=["Secret"]
143
143
  )
@@ -273,7 +273,7 @@ def setup(
273
273
  )
274
274
  else:
275
275
  ocm_map = None
276
- tf_namespaces_dicts = [ns.dict(by_alias=True) for ns in tf_namespaces]
276
+ tf_namespaces_dicts = [ns.model_dump(by_alias=True) for ns in tf_namespaces]
277
277
 
278
278
  provider_exclusions = settings.get("terraformResourcesProviderExclusions") or []
279
279
  ts.init_populate_specs(
@@ -295,16 +295,16 @@ def filter_tf_namespaces(
295
295
  ) -> list[NamespaceV1]:
296
296
  tf_namespaces = []
297
297
  for namespace_info in namespaces:
298
- if ob.is_namespace_deleted(namespace_info.dict(by_alias=True)):
298
+ if ob.is_namespace_deleted(namespace_info.model_dump(by_alias=True)):
299
299
  continue
300
- if not managed_external_resources(namespace_info.dict(by_alias=True)):
300
+ if not managed_external_resources(namespace_info.model_dump(by_alias=True)):
301
301
  continue
302
302
 
303
303
  if not account_names:
304
304
  tf_namespaces.append(namespace_info)
305
305
  continue
306
306
 
307
- specs = get_external_resource_specs(namespace_info.dict(by_alias=True))
307
+ specs = get_external_resource_specs(namespace_info.model_dump(by_alias=True))
308
308
  if not specs:
309
309
  tf_namespaces.append(namespace_info)
310
310
  continue
@@ -567,7 +567,7 @@ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
567
567
  }
568
568
  for ns_info in get_tf_namespaces():
569
569
  for spec in get_external_resource_specs(
570
- ns_info.dict(by_alias=True), provision_provider=PROVIDER_AWS
570
+ ns_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
571
571
  ):
572
572
  resource_paths = [
573
573
  spec.resource.get("defaults"),