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
@@ -1,25 +1,27 @@
1
1
  import logging
2
- import sys
2
+ from collections import defaultdict
3
3
  from collections.abc import (
4
4
  Callable,
5
5
  Iterable,
6
- Mapping,
7
6
  Sequence,
8
7
  )
8
+ from dataclasses import dataclass
9
+ from enum import StrEnum
9
10
  from typing import Any
10
11
 
11
12
  from sretoolbox.utils import threaded
12
13
 
13
14
  import reconcile.openshift_base as ob
14
15
  from reconcile.gql_definitions.common.namespaces_minimal import NamespaceV1
15
- from reconcile.status import ExitCodes
16
16
  from reconcile.typed_queries.app_interface_vault_settings import (
17
17
  get_app_interface_vault_settings,
18
18
  )
19
19
  from reconcile.typed_queries.namespaces_minimal import get_namespaces_minimal
20
20
  from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
21
21
  from reconcile.utils.defer import defer
22
- from reconcile.utils.oc_filters import filter_namespaces_by_cluster_and_namespace
22
+ from reconcile.utils.oc_filters import (
23
+ filter_namespaces_by_cluster_and_namespace,
24
+ )
23
25
  from reconcile.utils.oc_map import (
24
26
  OCLogMsg,
25
27
  OCMap,
@@ -30,113 +32,112 @@ from reconcile.utils.sharding import is_in_shard
30
32
 
31
33
  QONTRACT_INTEGRATION = "openshift-namespaces"
32
34
 
33
- NS_STATE_PRESENT = "present"
34
- NS_STATE_ABSENT = "absent"
35
-
36
- NS_ACTION_CREATE = "create"
37
- NS_ACTION_DELETE = "delete"
38
35
 
36
+ class Action(StrEnum):
37
+ CREATE = "create"
38
+ DELETE = "delete"
39
39
 
40
- DUPLICATES_LOG_MSG = "Found multiple definitions for the namespace {key}"
41
40
 
41
+ @dataclass(frozen=True)
42
+ class DesiredState:
43
+ cluster: str
44
+ namespace: str
45
+ delete: bool
42
46
 
43
- def get_desired_state(namespaces: Iterable[NamespaceV1]) -> list[dict[str, str]]:
44
- desired_state: list[dict[str, str]] = []
45
- for ns in namespaces:
46
- state = NS_STATE_PRESENT
47
- if ns.delete:
48
- state = NS_STATE_ABSENT
49
47
 
50
- desired_state.append({
51
- "cluster": ns.cluster.name,
52
- "namespace": ns.name,
53
- "desired_state": state,
54
- })
48
+ class NamespaceDuplicateError(Exception):
49
+ pass
55
50
 
56
- return desired_state
57
51
 
52
+ def get_namespaces(
53
+ cluster_name: Sequence[str] | None,
54
+ namespace_name: Sequence[str] | None,
55
+ ) -> tuple[list[NamespaceV1], list[NamespaceDuplicateError]]:
56
+ all_namespaces = get_namespaces_minimal()
58
57
 
59
- def get_shard_namespaces(
60
- namespaces: Iterable[NamespaceV1],
61
- ) -> tuple[list[NamespaceV1], bool]:
62
- # Structure holding duplicates by namespace key
63
- duplicates: dict[str, list[NamespaceV1]] = {}
64
- # namespace filtered list without duplicates
65
- filtered_ns: dict[str, NamespaceV1] = {}
66
-
67
- err = False
68
- for ns in namespaces:
69
- key = f"{ns.cluster.name}/{ns.name}"
58
+ namespaces_by_shard_key = defaultdict(list)
70
59
 
60
+ for namespace in all_namespaces:
61
+ key = f"{namespace.cluster.name}/{namespace.name}"
71
62
  if is_in_shard(key):
72
- if key not in filtered_ns:
73
- filtered_ns[key] = ns
74
- else:
75
- # Duplicated NS
76
- dupe_list_by_key = duplicates.setdefault(key, [])
77
- dupe_list_by_key.append(ns)
78
-
79
- for key, dupe_list in duplicates.items():
80
- dupe_list.append(filtered_ns[key])
81
- delete_flags = (
82
- [ns.delete for ns in dupe_list_by_key] if dupe_list_by_key else []
83
- )
63
+ namespaces_by_shard_key[key].append(namespace)
84
64
 
85
- if len(set(delete_flags)) > 1:
86
- # If true only some definitions in list have the delete flag.
87
- # this case will generate an error
88
- err = True
89
- # Remove the namespace found from the filtered list
90
- del filtered_ns[key]
91
- logging.error(DUPLICATES_LOG_MSG.format(key=key))
65
+ managed_namespaces = []
66
+ duplicate_errors = []
67
+
68
+ for key, namespaces in namespaces_by_shard_key.items():
69
+ if len(namespaces) == 1:
70
+ namespace = namespaces[0]
71
+ if not namespace.managed_by_external:
72
+ managed_namespaces.append(namespace)
92
73
  else:
93
- # If all namespaces have the same delete option
94
- # The action will be performaed
95
- logging.debug(DUPLICATES_LOG_MSG.format(key=key))
74
+ msg = f"Found multiple definitions for the namespace {key}"
75
+ duplicate_errors.append(NamespaceDuplicateError(msg))
76
+ logging.error(msg)
77
+
78
+ namespaces = filter_namespaces_by_cluster_and_namespace(
79
+ namespaces=managed_namespaces,
80
+ cluster_names=cluster_name,
81
+ namespace_names=namespace_name,
82
+ )
83
+
84
+ return namespaces, duplicate_errors
85
+
96
86
 
97
- return list(filtered_ns.values()), err
87
+ def build_desired_state(
88
+ namespaces: Iterable[NamespaceV1],
89
+ ) -> list[DesiredState]:
90
+ return [
91
+ DesiredState(
92
+ cluster=namespace.cluster.name,
93
+ namespace=namespace.name,
94
+ delete=namespace.delete or False,
95
+ )
96
+ for namespace in namespaces
97
+ ]
98
98
 
99
99
 
100
- def manage_namespaces(spec: Mapping[str, str], oc_map: OCMap, dry_run: bool) -> None:
101
- cluster = spec["cluster"]
102
- namespace = spec["namespace"]
103
- desired_state = spec["desired_state"]
100
+ def manage_namespace(
101
+ desired_state: DesiredState,
102
+ oc_map: OCMap,
103
+ dry_run: bool,
104
+ ) -> None:
105
+ namespace = desired_state.namespace
104
106
 
105
- oc = oc_map.get(cluster)
107
+ oc = oc_map.get(desired_state.cluster)
106
108
  if isinstance(oc, OCLogMsg):
107
109
  logging.log(level=oc.log_level, msg=oc.message)
108
- return None
110
+ return
109
111
 
110
- act = {NS_ACTION_CREATE: oc.new_project, NS_ACTION_DELETE: oc.delete_project}
112
+ current_delete = not oc.project_exists(namespace)
111
113
 
112
- exists = oc.project_exists(namespace)
113
- action = None
114
- if not exists and desired_state == NS_STATE_PRESENT:
115
- if namespace.startswith("openshift-"):
116
- raise ValueError('cannot request a project starting with "openshift-"')
117
- action = NS_ACTION_CREATE
118
- elif exists and desired_state == NS_STATE_ABSENT:
119
- action = NS_ACTION_DELETE
114
+ if desired_state.delete == current_delete:
115
+ return
120
116
 
121
- if action:
122
- logging.info([action, cluster, namespace])
123
- if not dry_run:
124
- act[action](namespace)
117
+ action = Action.DELETE if desired_state.delete else Action.CREATE
125
118
 
119
+ if namespace.startswith("openshift-"):
120
+ raise ValueError(f'cannot {action} a project starting with "openshift-"')
126
121
 
127
- def check_results(
128
- desired_state: Iterable[Mapping[str, str]], results: Iterable[Any]
129
- ) -> bool:
130
- err = False
122
+ logging.info([str(action), desired_state.cluster, namespace])
123
+ if not dry_run:
124
+ match action:
125
+ case Action.CREATE:
126
+ oc.new_project(namespace)
127
+ case Action.DELETE:
128
+ oc.delete_project(namespace)
129
+
130
+
131
+ def build_runtime_errors(
132
+ desired_state: Iterable[DesiredState],
133
+ results: Iterable[Any],
134
+ ) -> list[Exception]:
135
+ exceptions = []
131
136
  for s, e in zip(desired_state, results, strict=False):
132
137
  if isinstance(e, Exception):
133
- err = True
134
- msg = (
135
- f"cluster: {s['cluster']}, namespace: {s['namespace']}, "
136
- f"exception: {e!s}"
137
- )
138
- logging.error(msg)
139
- return err
138
+ e.add_note(f"cluster: {s.cluster}, namespace: {s.namespace}")
139
+ exceptions.append(e)
140
+ return exceptions
140
141
 
141
142
 
142
143
  @defer
@@ -149,15 +150,8 @@ def run(
149
150
  namespace_name: Sequence[str] | None = None,
150
151
  defer: Callable | None = None,
151
152
  ) -> None:
152
- all_namespaces = get_namespaces_minimal()
153
- shard_namespaces, duplicates = get_shard_namespaces(all_namespaces)
154
- namespaces = filter_namespaces_by_cluster_and_namespace(
155
- namespaces=shard_namespaces,
156
- cluster_names=cluster_name,
157
- namespace_names=namespace_name,
158
- )
159
-
160
- desired_state = get_desired_state(namespaces)
153
+ namespaces, duplicate_errors = get_namespaces(cluster_name, namespace_name)
154
+ desired_state = build_desired_state(namespaces)
161
155
 
162
156
  vault_settings = get_app_interface_vault_settings()
163
157
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
@@ -171,16 +165,17 @@ def run(
171
165
  thread_pool_size=thread_pool_size,
172
166
  init_projects=True,
173
167
  )
174
-
175
168
  if defer:
176
169
  defer(oc_map.cleanup)
177
170
 
178
171
  ob.publish_cluster_desired_metrics_from_state(
179
- desired_state, QONTRACT_INTEGRATION, "Namespace"
172
+ state=({"cluster": s.cluster} for s in desired_state),
173
+ integration=QONTRACT_INTEGRATION,
174
+ kind="Namespace",
180
175
  )
181
176
 
182
177
  results = threaded.run(
183
- manage_namespaces,
178
+ manage_namespace,
184
179
  desired_state,
185
180
  thread_pool_size,
186
181
  return_exceptions=True,
@@ -188,6 +183,7 @@ def run(
188
183
  oc_map=oc_map,
189
184
  )
190
185
 
191
- err = check_results(desired_state=desired_state, results=results)
192
- if err or duplicates:
193
- sys.exit(ExitCodes.ERROR)
186
+ runtime_errors = build_runtime_errors(desired_state, results)
187
+ errors = runtime_errors + duplicate_errors
188
+ if errors:
189
+ raise ExceptionGroup("Reconcile errors occurred", errors)
@@ -806,7 +806,11 @@ def fetch_data(
806
806
  init_api_resources=init_api_resources,
807
807
  )
808
808
  state_specs = ob.init_specs_to_fetch(
809
- ri, oc_map, namespaces=namespaces, override_managed_types=overrides
809
+ ri,
810
+ oc_map,
811
+ namespaces=namespaces,
812
+ override_managed_types=overrides,
813
+ cluster_scope_resource_validation=True,
810
814
  )
811
815
  threaded.run(fetch_states, state_specs, thread_pool_size, ri=ri, settings=settings)
812
816
 
@@ -861,7 +865,7 @@ def canonicalize_namespaces(
861
865
  elif providers[0] == "route":
862
866
  override = ["Route"]
863
867
  elif providers[0] == "prometheus-rule":
864
- override = ["PrometheusRule"]
868
+ override = ["PrometheusRule.monitoring.coreos.com"]
865
869
 
866
870
  namespace_info["openshiftResources"] = ors
867
871
  canonicalized_namespaces.append(namespace_info)
@@ -2,7 +2,7 @@ import logging
2
2
  import sys
3
3
  import time
4
4
  from collections.abc import Callable, Iterable, Mapping
5
- from typing import Any, cast
5
+ from typing import Any
6
6
 
7
7
  import reconcile.openshift_base as ob
8
8
  import reconcile.openshift_resources_base as orb
@@ -10,8 +10,8 @@ from reconcile.gql_definitions.common.rhcs_provider_settings import (
10
10
  RhcsProviderSettingsV1,
11
11
  )
12
12
  from reconcile.gql_definitions.rhcs.certs import (
13
- NamespaceOpenshiftResourceRhcsCertV1,
14
13
  NamespaceV1,
14
+ OpenshiftResourceRhcsCert,
15
15
  )
16
16
  from reconcile.gql_definitions.rhcs.certs import (
17
17
  query as rhcs_certs_query,
@@ -40,7 +40,6 @@ from reconcile.utils.vault import SecretNotFoundError, VaultClient
40
40
 
41
41
  QONTRACT_INTEGRATION = "openshift-rhcs-certs"
42
42
  QONTRACT_INTEGRATION_VERSION = make_semver(1, 9, 3)
43
- PROVIDERS = ["rhcs-cert"]
44
43
 
45
44
 
46
45
  def desired_state_shard_config() -> DesiredStateShardConfig:
@@ -67,22 +66,18 @@ class OpenshiftRhcsCertExpiration(GaugeMetric):
67
66
  return "qontract_reconcile_rhcs_cert_expiration_timestamp"
68
67
 
69
68
 
70
- def _is_rhcs_cert(obj: Any) -> bool:
71
- return getattr(obj, "provider", None) == "rhcs-cert"
72
-
73
-
74
69
  def get_namespaces_with_rhcs_certs(
75
70
  query_func: Callable,
76
71
  cluster_name: Iterable[str] | None = None,
77
72
  ) -> list[NamespaceV1]:
78
73
  result: list[NamespaceV1] = []
79
74
  for ns in rhcs_certs_query(query_func=query_func).namespaces or []:
80
- ob.aggregate_shared_resources_typed(cast("Any", ns)) # mypy: ignore[arg-type]
75
+ ob.aggregate_shared_resources_typed(ns)
81
76
  if (
82
77
  integration_is_enabled(QONTRACT_INTEGRATION, ns.cluster)
83
78
  and not bool(ns.delete)
84
79
  and (not cluster_name or ns.cluster.name in cluster_name)
85
- and any(_is_rhcs_cert(r) for r in ns.openshift_resources or [])
80
+ and ns.openshift_resources
86
81
  ):
87
82
  result.append(ns)
88
83
  return result
@@ -105,7 +100,7 @@ def construct_rhcs_cert_oc_secret(
105
100
 
106
101
  def cert_expires_within_threshold(
107
102
  ns: NamespaceV1,
108
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
103
+ cert_resource: OpenshiftResourceRhcsCert,
109
104
  vault_cert_secret: Mapping[str, Any],
110
105
  ) -> bool:
111
106
  auto_renew_threshold_days = cert_resource.auto_renew_threshold_days or 7
@@ -121,7 +116,7 @@ def cert_expires_within_threshold(
121
116
 
122
117
  def get_vault_cert_secret(
123
118
  ns: NamespaceV1,
124
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
119
+ cert_resource: OpenshiftResourceRhcsCert,
125
120
  vault: VaultClient,
126
121
  vault_base_path: str,
127
122
  ) -> dict | None:
@@ -140,7 +135,7 @@ def get_vault_cert_secret(
140
135
  def generate_vault_cert_secret(
141
136
  dry_run: bool,
142
137
  ns: NamespaceV1,
143
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
138
+ cert_resource: OpenshiftResourceRhcsCert,
144
139
  vault: VaultClient,
145
140
  vault_base_path: str,
146
141
  issuer_url: str,
@@ -149,7 +144,7 @@ def generate_vault_cert_secret(
149
144
  logging.info(
150
145
  f"Creating cert with service account credentials for '{cert_resource.service_account_name}'. cluster='{ns.cluster.name}', namespace='{ns.name}', secret='{cert_resource.secret_name}'"
151
146
  )
152
- sa_password = vault.read(cert_resource.service_account_password.dict())
147
+ sa_password = vault.read(cert_resource.service_account_password.model_dump())
153
148
  if dry_run:
154
149
  rhcs_cert = RhcsV2Cert(
155
150
  certificate="PLACEHOLDER_CERT",
@@ -171,18 +166,18 @@ def generate_vault_cert_secret(
171
166
  )
172
167
  vault.write(
173
168
  secret={
174
- "data": rhcs_cert.dict(by_alias=True),
169
+ "data": rhcs_cert.model_dump(by_alias=True),
175
170
  "path": f"{vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}",
176
171
  },
177
172
  decode_base64=False,
178
173
  )
179
- return rhcs_cert.dict(by_alias=True)
174
+ return rhcs_cert.model_dump(by_alias=True)
180
175
 
181
176
 
182
177
  def fetch_openshift_resource_for_cert_resource(
183
178
  dry_run: bool,
184
179
  ns: NamespaceV1,
185
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
180
+ cert_resource: OpenshiftResourceRhcsCert,
186
181
  vault: VaultClient,
187
182
  rhcs_settings: RhcsProviderSettingsV1,
188
183
  ) -> OR:
@@ -231,18 +226,13 @@ def fetch_desired_state(
231
226
  cert_provider = get_rhcs_provider_settings(query_func=query_func)
232
227
  for ns in namespaces:
233
228
  for cert_resource in ns.openshift_resources or []:
234
- if _is_rhcs_cert(cert_resource):
235
- ri.add_desired_resource(
236
- cluster=ns.cluster.name,
237
- namespace=ns.name,
238
- resource=fetch_openshift_resource_for_cert_resource(
239
- dry_run,
240
- ns,
241
- cast("NamespaceOpenshiftResourceRhcsCertV1", cert_resource),
242
- vault,
243
- cert_provider,
244
- ),
245
- )
229
+ ri.add_desired_resource(
230
+ cluster=ns.cluster.name,
231
+ namespace=ns.name,
232
+ resource=fetch_openshift_resource_for_cert_resource(
233
+ dry_run, ns, cert_resource, vault, cert_provider
234
+ ),
235
+ )
246
236
 
247
237
 
248
238
  @defer
@@ -277,7 +267,7 @@ def run(
277
267
  state_specs = ob.init_specs_to_fetch(
278
268
  ri,
279
269
  oc_map,
280
- namespaces=[ns.dict(by_alias=True) for ns in namespaces],
270
+ namespaces=[ns.model_dump(by_alias=True) for ns in namespaces],
281
271
  override_managed_types=["Secret"],
282
272
  )
283
273
  for spec in state_specs:
@@ -295,3 +285,11 @@ def run(
295
285
  ob.publish_metrics(ri, QONTRACT_INTEGRATION)
296
286
  if ri.has_error_registered():
297
287
  sys.exit(1)
288
+
289
+
290
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
291
+ if not (query_func := kwargs.get("query_func")):
292
+ query_func = gql.get_api().query
293
+
294
+ cluster_name = kwargs.get("cluster_name")
295
+ return {"namespace": get_namespaces_with_rhcs_certs(query_func, cluster_name)}
@@ -33,14 +33,11 @@ QONTRACT_INTEGRATION = "openshift-rolebindings"
33
33
  QONTRACT_INTEGRATION_VERSION = make_semver(0, 3, 0)
34
34
 
35
35
 
36
- class OCResource(BaseModel):
36
+ class OCResource(BaseModel, arbitrary_types_allowed=True):
37
37
  resource: OR
38
38
  resource_name: str
39
39
  privileged: bool
40
40
 
41
- class Config:
42
- arbitrary_types_allowed = True
43
-
44
41
 
45
42
  @dataclass
46
43
  class ServiceAccountSpec:
@@ -61,7 +58,7 @@ class ServiceAccountSpec:
61
58
  ]
62
59
 
63
60
 
64
- class RoleBindingSpec(BaseModel):
61
+ class RoleBindingSpec(BaseModel, validate_by_alias=True, arbitrary_types_allowed=True):
65
62
  role_name: str
66
63
  role_kind: str
67
64
  namespace: NamespaceV1
@@ -70,9 +67,6 @@ class RoleBindingSpec(BaseModel):
70
67
  usernames: set[str]
71
68
  openshift_service_accounts: list[ServiceAccountSpec]
72
69
 
73
- class Config:
74
- arbitrary_types_allowed = True
75
-
76
70
  def get_users_desired_state(self) -> list[dict[str, str]]:
77
71
  return [
78
72
  {"cluster": self.cluster.name, "user": username}
@@ -93,7 +87,9 @@ class RoleBindingSpec(BaseModel):
93
87
  if not (access.role or access.cluster_role):
94
88
  return None
95
89
  privileged = access.namespace.cluster_admin or False
96
- auth_dict = [auth.dict(by_alias=True) for auth in access.namespace.cluster.auth]
90
+ auth_dict = [
91
+ auth.model_dump(by_alias=True) for auth in access.namespace.cluster.auth
92
+ ]
97
93
  usernames = RoleBindingSpec.get_usernames_from_users(
98
94
  users,
99
95
  ob.determine_user_keys_for_access(
@@ -290,7 +286,7 @@ def is_valid_namespace(namespace: NamespaceV1 | CommonNamespaceV1) -> bool:
290
286
  return (
291
287
  bool(namespace.managed_roles)
292
288
  and is_in_shard(f"{namespace.cluster.name}/{namespace.name}")
293
- and not ob.is_namespace_deleted(namespace.dict(by_alias=True))
289
+ and not ob.is_namespace_deleted(namespace.model_dump(by_alias=True))
294
290
  )
295
291
 
296
292
 
@@ -304,7 +300,7 @@ def run(
304
300
  defer: Callable | None = None,
305
301
  ) -> None:
306
302
  namespaces = [
307
- namespace.dict(by_alias=True, exclude={"openshift_resources"})
303
+ namespace.model_dump(by_alias=True, exclude={"openshift_resources"})
308
304
  for namespace in get_namespaces()
309
305
  if is_valid_namespace(namespace)
310
306
  ]
@@ -150,7 +150,7 @@ def run(
150
150
  + "when using slack notifications"
151
151
  )
152
152
  slack = slackapi_from_slack_workspace(
153
- saas_file.slack.dict(by_alias=True),
153
+ saas_file.slack.model_dump(by_alias=True),
154
154
  secret_reader,
155
155
  QONTRACT_INTEGRATION,
156
156
  init_usergroups=False,
@@ -224,7 +224,7 @@ def run(
224
224
  default=False,
225
225
  )
226
226
  ri, oc_map = ob.fetch_current_state(
227
- namespaces=[ns.dict(by_alias=True) for ns in saasherder.namespaces],
227
+ namespaces=[ns.model_dump(by_alias=True) for ns in saasherder.namespaces],
228
228
  thread_pool_size=thread_pool_size,
229
229
  integration=QONTRACT_INTEGRATION,
230
230
  integration_version=QONTRACT_INTEGRATION_VERSION,
@@ -319,14 +319,13 @@ def run(
319
319
  openshift_saas_deploy_trigger_upstream_jobs.QONTRACT_INTEGRATION,
320
320
  openshift_saas_deploy_trigger_images.QONTRACT_INTEGRATION,
321
321
  ]
322
- scan = (
322
+ if (
323
323
  not dry_run
324
324
  and len(saas_files) == 1
325
325
  and trigger_integration
326
326
  and trigger_integration in allowed_integration
327
327
  and trigger_reason
328
- )
329
- if scan:
328
+ ):
330
329
  saas_file = saas_files[0]
331
330
  owners = saas_file.app.service_owners or []
332
331
  emails = " ".join([o.email for o in owners])
@@ -34,7 +34,7 @@ class Definition(BaseModel):
34
34
  class State(BaseModel):
35
35
  saas_file_path: str
36
36
  saas_file_name: str
37
- saas_file_deploy_resources: DeployResourcesV1 | None
37
+ saas_file_deploy_resources: DeployResourcesV1 | None = None
38
38
  resource_template_name: str
39
39
  cluster: str
40
40
  namespace: str
@@ -44,10 +44,10 @@ class State(BaseModel):
44
44
  parameters: dict[str, Any]
45
45
  secret_parameters: dict[str, VaultSecret]
46
46
  saas_file_definitions: Definition
47
- upstream: SaasResourceTemplateTargetUpstreamV1 | None
48
- disable: bool | None
49
- delete: bool | None
50
- target_path: str | None
47
+ upstream: SaasResourceTemplateTargetUpstreamV1 | None = None
48
+ disable: bool | None = None
49
+ delete: bool | None = None
50
+ target_path: str | None = None
51
51
 
52
52
 
53
53
  def osd_run_wrapper(
@@ -213,11 +213,13 @@ def run(
213
213
  saas_file_list = SaasFileList()
214
214
  desired_saas_file_state = collect_state(saas_file_list.saas_files)
215
215
  # compare dicts against dicts which is much faster than comparing BaseModel objects
216
- comparison_saas_file_state_dicts = [s.dict() for s in comparison_saas_file_state]
216
+ comparison_saas_file_state_dicts = [
217
+ s.model_dump() for s in comparison_saas_file_state
218
+ ]
217
219
  saas_file_state_diffs = [
218
220
  s
219
221
  for s in desired_saas_file_state
220
- if s.dict() not in comparison_saas_file_state_dicts
222
+ if s.model_dump() not in comparison_saas_file_state_dicts
221
223
  ]
222
224
  if not saas_file_state_diffs:
223
225
  return
@@ -177,7 +177,7 @@ def canonicalize_namespaces(namespaces: Iterable[NamespaceV1]) -> list[Namespace
177
177
  key = f"{sat.namespace.cluster.name}/{sat.namespace.name}"
178
178
  if key not in canonicalized_namespaces:
179
179
  canonicalized_namespaces[key] = NamespaceV1(
180
- **sat.namespace.dict(by_alias=True),
180
+ **sat.namespace.model_dump(by_alias=True),
181
181
  sharedResources=None,
182
182
  openshiftServiceAccountTokens=None,
183
183
  )
@@ -217,7 +217,7 @@ def run(
217
217
  get_namespaces_with_serviceaccount_tokens(gql_api.query)
218
218
  )
219
219
  ri, oc_map = ob.fetch_current_state(
220
- namespaces=[ns.dict(by_alias=True) for ns in namespaces],
220
+ namespaces=[ns.model_dump(by_alias=True) for ns in namespaces],
221
221
  thread_pool_size=thread_pool_size,
222
222
  integration=QONTRACT_INTEGRATION,
223
223
  integration_version=QONTRACT_INTEGRATION_VERSION,
@@ -185,7 +185,7 @@ def run(
185
185
  if defer:
186
186
  defer(oc_map.cleanup)
187
187
 
188
- cluster_like_objects = [cluster.dict(by_alias=True) for cluster in clusters]
188
+ cluster_like_objects = [cluster.model_dump(by_alias=True) for cluster in clusters]
189
189
  ocm_map = OCMMap(
190
190
  clusters=cluster_like_objects,
191
191
  integration=QONTRACT_INTEGRATION,
reconcile/oum/labelset.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from collections import defaultdict
2
+ from typing import Annotated
2
3
 
3
4
  from pydantic import BaseModel
4
5
 
@@ -22,9 +23,10 @@ class _GroupMappingLabelset(BaseModel):
22
23
  the sre-capabilities.user-mgmt.$provider prefix.
23
24
  """
24
25
 
25
- authz_roles: dict[str, CSV] | None = sre_capability_labels.labelset_groupfield(
26
- group_prefix="authz."
27
- )
26
+ authz_roles: Annotated[
27
+ dict[str, CSV] | None,
28
+ sre_capability_labels.labelset_groupfield(group_prefix="authz."),
29
+ ]
28
30
 
29
31
 
30
32
  def build_cluster_config_from_labels(
reconcile/oum/models.py CHANGED
@@ -56,7 +56,7 @@ class ClusterUserManagementSpec(BaseModel):
56
56
  errors: list[ClusterError] = Field(default_factory=list)
57
57
 
58
58
 
59
- class ClusterRoleReconcileResult(BaseModel):
59
+ class ClusterRoleReconcileResult(BaseModel, arbitrary_types_allowed=True):
60
60
  """
61
61
  Holds the result of a cluster role reconciliation.
62
62
  """
@@ -64,6 +64,3 @@ class ClusterRoleReconcileResult(BaseModel):
64
64
  users_added: int = 0
65
65
  users_removed: int = 0
66
66
  error: Exception | None = None
67
-
68
- class Config:
69
- arbitrary_types_allowed = True
@@ -56,7 +56,7 @@ class Test(BaseModel):
56
56
  rule_path: str
57
57
  rule: dict
58
58
  rule_length: int
59
- tests: list[TestContent] | None
59
+ tests: list[TestContent] | None = None
60
60
  result: CommandExecutionResult | None = None
61
61
  promtool_version: str
62
62
 
@@ -76,7 +76,7 @@ def fetch_rule_and_tests(
76
76
  openshift_resource = orb.fetch_openshift_resource(
77
77
  resource=rule.resource,
78
78
  parent=rule.namespace,
79
- settings=vault_settings.dict(by_alias=True),
79
+ settings=vault_settings.model_dump(by_alias=True),
80
80
  )
81
81
 
82
82
  rule_body = openshift_resource.body
@@ -96,7 +96,7 @@ def fetch_rule_and_tests(
96
96
  test_raw_yaml = process_extracurlyjinja2_template(
97
97
  body=test_raw_yaml,
98
98
  vars=variables,
99
- settings=vault_settings.dict(by_alias=True),
99
+ settings=vault_settings.model_dump(by_alias=True),
100
100
  )
101
101
 
102
102
  test_yaml_spec = yaml.safe_load(test_raw_yaml)