qontract-reconcile 0.10.2.dev334__py3-none-any.whl → 0.10.2.dev439__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.

Potentially problematic release.


This version of qontract-reconcile might be problematic. Click here for more details.

Files changed (370) hide show
  1. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +366 -361
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +18 -12
  5. reconcile/aus/base.py +134 -32
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +3 -1
  8. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  9. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  10. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  11. reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
  12. reconcile/automated_actions/config/integration.py +16 -4
  13. reconcile/aws_account_manager/integration.py +8 -8
  14. reconcile/aws_account_manager/reconciler.py +3 -3
  15. reconcile/aws_ami_cleanup/integration.py +8 -12
  16. reconcile/aws_ami_share.py +69 -62
  17. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  18. reconcile/aws_ecr_image_pull_secrets.py +3 -3
  19. reconcile/aws_iam_keys.py +1 -0
  20. reconcile/aws_saml_idp/integration.py +12 -4
  21. reconcile/aws_saml_roles/integration.py +32 -25
  22. reconcile/aws_version_sync/integration.py +6 -12
  23. reconcile/change_owners/bundle.py +3 -3
  24. reconcile/change_owners/change_log_tracking.py +3 -2
  25. reconcile/change_owners/change_owners.py +1 -1
  26. reconcile/change_owners/diff.py +2 -4
  27. reconcile/checkpoint.py +11 -3
  28. reconcile/cli.py +111 -18
  29. reconcile/dashdotdb_dora.py +5 -12
  30. reconcile/dashdotdb_slo.py +1 -1
  31. reconcile/database_access_manager.py +123 -117
  32. reconcile/dynatrace_token_provider/integration.py +1 -1
  33. reconcile/endpoints_discovery/integration.py +4 -1
  34. reconcile/endpoints_discovery/merge_request.py +1 -1
  35. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  36. reconcile/external_resources/factories.py +5 -12
  37. reconcile/external_resources/integration.py +1 -1
  38. reconcile/external_resources/manager.py +8 -5
  39. reconcile/external_resources/meta.py +0 -1
  40. reconcile/external_resources/metrics.py +1 -1
  41. reconcile/external_resources/model.py +20 -20
  42. reconcile/external_resources/reconciler.py +7 -4
  43. reconcile/external_resources/secrets_sync.py +8 -11
  44. reconcile/external_resources/state.py +26 -16
  45. reconcile/fleet_labeler/integration.py +1 -1
  46. reconcile/gabi_authorized_users.py +8 -5
  47. reconcile/gcp_image_mirror.py +2 -2
  48. reconcile/github_org.py +1 -1
  49. reconcile/github_owners.py +4 -0
  50. reconcile/gitlab_housekeeping.py +13 -15
  51. reconcile/gitlab_members.py +6 -12
  52. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  53. reconcile/gitlab_owners.py +15 -11
  54. reconcile/gitlab_permissions.py +8 -12
  55. reconcile/glitchtip_project_alerts/integration.py +3 -1
  56. reconcile/gql_definitions/acs/acs_instances.py +10 -10
  57. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  58. reconcile/gql_definitions/acs/acs_rbac.py +6 -6
  59. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
  60. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
  61. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
  62. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  63. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  64. reconcile/gql_definitions/automated_actions/instance.py +51 -12
  65. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
  66. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
  67. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
  68. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
  69. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
  70. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  71. reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
  72. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  73. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  74. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
  75. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
  76. reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
  77. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  78. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  79. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  80. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  81. reconcile/gql_definitions/common/app_interface_roles.py +120 -0
  82. reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
  83. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  84. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  85. reconcile/gql_definitions/common/apps.py +5 -5
  86. reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
  87. reconcile/gql_definitions/common/aws_vpcs.py +11 -11
  88. reconcile/gql_definitions/common/clusters.py +37 -35
  89. reconcile/gql_definitions/common/clusters_minimal.py +14 -14
  90. reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
  91. reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
  92. reconcile/gql_definitions/common/github_orgs.py +10 -10
  93. reconcile/gql_definitions/common/jira_settings.py +10 -10
  94. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  95. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  96. reconcile/gql_definitions/common/namespaces.py +42 -44
  97. reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
  98. reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
  99. reconcile/gql_definitions/common/ocm_environments.py +19 -19
  100. reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
  101. reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
  102. reconcile/gql_definitions/common/pipeline_providers.py +29 -29
  103. reconcile/gql_definitions/common/quay_instances.py +5 -5
  104. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  105. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  106. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  107. reconcile/gql_definitions/common/saas_files.py +44 -44
  108. reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
  109. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  110. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  111. reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
  112. reconcile/gql_definitions/common/state_aws_account.py +7 -8
  113. reconcile/gql_definitions/common/users.py +5 -5
  114. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  115. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  116. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  117. reconcile/gql_definitions/cost_report/settings.py +9 -9
  118. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
  119. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
  120. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  121. reconcile/gql_definitions/email_sender/apps.py +5 -5
  122. reconcile/gql_definitions/email_sender/emails.py +8 -8
  123. reconcile/gql_definitions/email_sender/users.py +6 -6
  124. reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
  125. reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
  126. reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
  127. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
  128. reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
  129. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  130. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
  131. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  132. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  133. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  135. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  136. reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
  137. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  138. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  139. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  140. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  141. reconcile/gql_definitions/fragments/disable.py +5 -5
  142. reconcile/gql_definitions/fragments/email_service.py +5 -5
  143. reconcile/gql_definitions/fragments/email_user.py +5 -5
  144. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  145. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  146. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  147. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  148. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  149. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  150. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  151. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  152. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  153. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  154. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  155. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  156. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  157. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  158. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  159. reconcile/gql_definitions/fragments/user.py +5 -5
  160. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  161. reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
  162. reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
  163. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
  164. reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
  165. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
  166. reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
  167. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
  168. reconcile/gql_definitions/integrations/integrations.py +48 -51
  169. reconcile/gql_definitions/introspection.json +3207 -1683
  170. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
  171. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
  172. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  173. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
  174. reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
  175. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  176. reconcile/gql_definitions/ldap_groups/settings.py +9 -9
  177. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  178. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  179. reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
  180. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  181. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
  182. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  183. reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
  184. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
  185. reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
  186. reconcile/gql_definitions/rhcs/certs.py +33 -87
  187. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  188. reconcile/gql_definitions/rhidp/organizations.py +18 -18
  189. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  190. reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
  191. reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
  192. reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
  193. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  194. reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
  195. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  196. reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
  197. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  198. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  199. reconcile/gql_definitions/status_board/status_board.py +6 -7
  200. reconcile/gql_definitions/statuspage/statuspages.py +9 -9
  201. reconcile/gql_definitions/templating/template_collection.py +5 -5
  202. reconcile/gql_definitions/templating/templates.py +5 -5
  203. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
  204. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
  205. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
  206. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
  207. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
  208. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
  209. reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
  210. reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
  211. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  212. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +440 -407
  213. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
  214. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
  215. reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
  216. reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
  217. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
  218. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  219. reconcile/integrations_manager.py +3 -3
  220. reconcile/jenkins_worker_fleets.py +10 -8
  221. reconcile/jira_permissions_validator.py +237 -122
  222. reconcile/ldap_groups/integration.py +1 -1
  223. reconcile/ocm/types.py +35 -56
  224. reconcile/ocm_aws_infrastructure_access.py +1 -1
  225. reconcile/ocm_clusters.py +4 -4
  226. reconcile/ocm_labels/integration.py +3 -2
  227. reconcile/ocm_machine_pools.py +33 -27
  228. reconcile/openshift_base.py +113 -5
  229. reconcile/openshift_cluster_bots.py +3 -2
  230. reconcile/openshift_namespace_labels.py +1 -1
  231. reconcile/openshift_namespaces.py +97 -101
  232. reconcile/openshift_resources_base.py +6 -2
  233. reconcile/openshift_rhcs_certs.py +74 -37
  234. reconcile/openshift_rolebindings.py +230 -130
  235. reconcile/openshift_saas_deploy.py +6 -7
  236. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  237. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  238. reconcile/openshift_serviceaccount_tokens.py +2 -2
  239. reconcile/openshift_upgrade_watcher.py +4 -4
  240. reconcile/openshift_users.py +5 -3
  241. reconcile/oum/labelset.py +5 -3
  242. reconcile/oum/models.py +1 -4
  243. reconcile/prometheus_rules_tester/integration.py +3 -3
  244. reconcile/quay_mirror.py +1 -1
  245. reconcile/queries.py +131 -0
  246. reconcile/rhidp/common.py +3 -5
  247. reconcile/rhidp/sso_client/base.py +16 -5
  248. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  249. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  250. reconcile/skupper_network/integration.py +2 -2
  251. reconcile/slack_usergroups.py +35 -14
  252. reconcile/sql_query.py +1 -0
  253. reconcile/status_board.py +6 -6
  254. reconcile/statuspage/atlassian.py +7 -7
  255. reconcile/statuspage/integrations/maintenances.py +4 -3
  256. reconcile/statuspage/page.py +4 -9
  257. reconcile/statuspage/status.py +5 -8
  258. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +5 -1
  259. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
  260. reconcile/templating/lib/merge_request_manager.py +2 -2
  261. reconcile/templating/lib/rendering.py +3 -3
  262. reconcile/templating/renderer.py +12 -13
  263. reconcile/terraform_aws_route53.py +7 -1
  264. reconcile/terraform_cloudflare_dns.py +3 -3
  265. reconcile/terraform_cloudflare_resources.py +5 -5
  266. reconcile/terraform_cloudflare_users.py +3 -2
  267. reconcile/terraform_init/integration.py +187 -23
  268. reconcile/terraform_repo.py +16 -12
  269. reconcile/terraform_resources.py +17 -7
  270. reconcile/terraform_tgw_attachments.py +27 -19
  271. reconcile/terraform_users.py +7 -0
  272. reconcile/terraform_vpc_peerings.py +14 -3
  273. reconcile/terraform_vpc_resources/integration.py +20 -8
  274. reconcile/typed_queries/app_interface_roles.py +10 -0
  275. reconcile/typed_queries/aws_account_tags.py +41 -0
  276. reconcile/typed_queries/cost_report/app_names.py +1 -1
  277. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  278. reconcile/typed_queries/saas_files.py +13 -13
  279. reconcile/typed_queries/status_board.py +2 -2
  280. reconcile/unleash_feature_toggles/integration.py +4 -2
  281. reconcile/utils/acs/base.py +6 -3
  282. reconcile/utils/acs/policies.py +2 -2
  283. reconcile/utils/aggregated_list.py +4 -3
  284. reconcile/utils/aws_api.py +51 -20
  285. reconcile/utils/aws_api_typed/api.py +38 -9
  286. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  287. reconcile/utils/aws_api_typed/logs.py +73 -0
  288. reconcile/utils/aws_api_typed/organization.py +4 -2
  289. reconcile/utils/binary.py +7 -12
  290. reconcile/utils/datetime_util.py +67 -0
  291. reconcile/utils/deadmanssnitch_api.py +1 -1
  292. reconcile/utils/differ.py +2 -3
  293. reconcile/utils/early_exit_cache.py +11 -12
  294. reconcile/utils/expiration.py +7 -3
  295. reconcile/utils/external_resource_spec.py +24 -1
  296. reconcile/utils/filtering.py +1 -1
  297. reconcile/utils/gitlab_api.py +7 -5
  298. reconcile/utils/glitchtip/client.py +6 -2
  299. reconcile/utils/glitchtip/models.py +25 -28
  300. reconcile/utils/gql.py +4 -7
  301. reconcile/utils/helm.py +2 -1
  302. reconcile/utils/helpers.py +1 -1
  303. reconcile/utils/instrumented_wrappers.py +1 -1
  304. reconcile/utils/internal_groups/client.py +2 -2
  305. reconcile/utils/internal_groups/models.py +8 -17
  306. reconcile/utils/jinja2/utils.py +6 -8
  307. reconcile/utils/jira_client.py +82 -63
  308. reconcile/utils/jjb_client.py +28 -15
  309. reconcile/utils/jobcontroller/controller.py +2 -2
  310. reconcile/utils/jobcontroller/models.py +17 -1
  311. reconcile/utils/json.py +74 -0
  312. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  313. reconcile/utils/membershipsources/models.py +16 -23
  314. reconcile/utils/membershipsources/resolver.py +4 -2
  315. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  316. reconcile/utils/merge_request_manager/parser.py +6 -6
  317. reconcile/utils/metrics.py +5 -5
  318. reconcile/utils/models.py +304 -82
  319. reconcile/utils/mr/app_interface_reporter.py +2 -2
  320. reconcile/utils/mr/base.py +2 -2
  321. reconcile/utils/mr/notificator.py +3 -3
  322. reconcile/utils/mr/update_access_report_base.py +3 -4
  323. reconcile/utils/mr/user_maintenance.py +3 -2
  324. reconcile/utils/oc.py +249 -203
  325. reconcile/utils/oc_filters.py +3 -3
  326. reconcile/utils/ocm/addons.py +0 -1
  327. reconcile/utils/ocm/base.py +18 -21
  328. reconcile/utils/ocm/cluster_groups.py +1 -1
  329. reconcile/utils/ocm/identity_providers.py +2 -2
  330. reconcile/utils/ocm/labels.py +1 -1
  331. reconcile/utils/ocm/products.py +9 -3
  332. reconcile/utils/ocm/search_filters.py +3 -6
  333. reconcile/utils/ocm/service_log.py +4 -6
  334. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  335. reconcile/utils/openshift_resource.py +10 -5
  336. reconcile/utils/output.py +3 -2
  337. reconcile/utils/pagerduty_api.py +10 -7
  338. reconcile/utils/promotion_state.py +6 -11
  339. reconcile/utils/raw_github_api.py +1 -1
  340. reconcile/utils/rhcsv2_certs.py +138 -35
  341. reconcile/utils/rosa/session.py +16 -0
  342. reconcile/utils/runtime/integration.py +2 -3
  343. reconcile/utils/runtime/runner.py +2 -2
  344. reconcile/utils/saasherder/interfaces.py +13 -20
  345. reconcile/utils/saasherder/models.py +25 -21
  346. reconcile/utils/saasherder/saasherder.py +55 -31
  347. reconcile/utils/slack_api.py +26 -4
  348. reconcile/utils/sloth.py +224 -0
  349. reconcile/utils/sqs_gateway.py +2 -1
  350. reconcile/utils/state.py +2 -1
  351. reconcile/utils/structs.py +1 -1
  352. reconcile/utils/terraform_client.py +5 -4
  353. reconcile/utils/terrascript_aws_client.py +192 -114
  354. reconcile/utils/unleash/server.py +2 -8
  355. reconcile/utils/vault.py +5 -12
  356. reconcile/utils/vcs.py +8 -8
  357. reconcile/vault_replication.py +107 -42
  358. tools/app_interface_reporter.py +4 -4
  359. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  360. tools/cli_commands/cost_report/view.py +7 -6
  361. tools/cli_commands/erv2.py +1 -1
  362. tools/cli_commands/systems_and_tools.py +5 -1
  363. tools/qontract_cli.py +31 -18
  364. tools/template_validation.py +3 -1
  365. reconcile/gql_definitions/cna/__init__.py +0 -0
  366. reconcile/gql_definitions/cna/queries/__init__.py +0 -0
  367. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  368. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  369. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
  370. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
@@ -24,7 +24,7 @@ from reconcile.typed_queries.jiralert_settings import get_jiralert_settings
24
24
  from reconcile.utils import gql, metrics
25
25
  from reconcile.utils.defer import defer
26
26
  from reconcile.utils.disabled_integrations import integration_is_enabled
27
- from reconcile.utils.jira_client import JiraClient, JiraWatcherSettings
27
+ from reconcile.utils.jira_client import IssueType, JiraClient, JiraWatcherSettings
28
28
  from reconcile.utils.runtime.integration import DesiredStateShardConfig
29
29
  from reconcile.utils.secret_reader import SecretReaderBase, create_secret_reader
30
30
  from reconcile.utils.semver_helper import make_semver
@@ -33,8 +33,6 @@ from reconcile.utils.state import State, init_state
33
33
  QONTRACT_INTEGRATION = "jira-permissions-validator"
34
34
  QONTRACT_INTEGRATION_VERSION = make_semver(1, 2, 0)
35
35
 
36
- NameToIdMap = dict[str, str]
37
-
38
36
 
39
37
  class BaseMetric(BaseModel):
40
38
  """Base class for metrics"""
@@ -64,9 +62,6 @@ class ValidationError(IntFlag):
64
62
  PROJECT_ARCHIVED = auto()
65
63
 
66
64
 
67
- CustomIssueFields = dict[str, dict[str, str]]
68
-
69
-
70
65
  class RunnerParams(TypedDict):
71
66
  boards: list[JiraBoardV1]
72
67
  board_check_interval_sec: int
@@ -77,6 +72,179 @@ class CacheSource(TypedDict):
77
72
  boards: list
78
73
 
79
74
 
75
+ NameToIdMap = dict[str, str]
76
+ CustomIssueFields = dict[str, dict[str, str]]
77
+ NO_ERRORS = ValidationError(0)
78
+
79
+
80
+ def _validate_project_archived(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
81
+ """Validate that the project is not archived."""
82
+ if jira.is_archived:
83
+ logging.error(f"[{board.name}] project is archived")
84
+ return ValidationError.PROJECT_ARCHIVED
85
+ return NO_ERRORS
86
+
87
+
88
+ def _validate_permissions(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
89
+ """Validate that the bot has necessary permissions to create and transition issues."""
90
+ error = NO_ERRORS
91
+
92
+ if not jira.can_create_issues():
93
+ logging.error(f"[{board.name}] can not create issues in project")
94
+ error |= ValidationError.CANT_CREATE_ISSUE
95
+
96
+ if not jira.can_transition_issues():
97
+ logging.error(
98
+ f"[{board.name}] AppSRE Jira Bot user does not have the permission to change the issue status."
99
+ )
100
+ error |= ValidationError.CANT_TRANSITION_ISSUES
101
+
102
+ return error
103
+
104
+
105
+ def _validate_components(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
106
+ """Validate that all escalation policy components exist in the project."""
107
+ error = NO_ERRORS
108
+ components = jira.components()
109
+
110
+ for escalation_policy in board.escalation_policies or []:
111
+ for jira_component in escalation_policy.channels.jira_components or []:
112
+ if jira_component not in components:
113
+ logging.error(
114
+ f"[{board.name}] escalation policy '{escalation_policy.name}' references a non existing Jira component "
115
+ f"'{jira_component}'. Valid components: {components}"
116
+ )
117
+ error |= ValidationError.INVALID_COMPONENT
118
+
119
+ return error
120
+
121
+
122
+ def _validate_issue_type(
123
+ jira: JiraClient, board: JiraBoardV1, default_issue_type: str
124
+ ) -> tuple[ValidationError, IssueType | None]:
125
+ """Validate that the issue type exists and has statuses."""
126
+ error = NO_ERRORS
127
+ issue_type = board.issue_type or default_issue_type
128
+ project_issue_type = jira.get_issue_type(issue_type)
129
+
130
+ if not project_issue_type:
131
+ project_issue_types_str = ", ".join(t.name for t in jira.project_issue_types())
132
+ logging.error(
133
+ f"[{board.name}] '{issue_type}' is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
134
+ )
135
+ error |= ValidationError.INVALID_ISSUE_TYPE
136
+ return error, None
137
+
138
+ if not project_issue_type.statuses:
139
+ logging.error(
140
+ f"[{board.name}] '{issue_type}' doesn't have any status. Choose a different issue type."
141
+ )
142
+ error |= ValidationError.INVALID_ISSUE_TYPE
143
+
144
+ return error, project_issue_type
145
+
146
+
147
+ def _validate_issue_states(
148
+ board: JiraBoardV1, project_issue_type: IssueType, default_reopen_state: str
149
+ ) -> ValidationError:
150
+ """Validate that reopen and resolve states are valid for the issue type."""
151
+ error = NO_ERRORS
152
+
153
+ reopen_state = board.issue_reopen_state or default_reopen_state
154
+ if reopen_state.lower() not in [t.lower() for t in project_issue_type.statuses]:
155
+ logging.error(
156
+ f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
157
+ )
158
+ error |= ValidationError.INVALID_ISSUE_STATE
159
+
160
+ if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
161
+ t.lower() for t in project_issue_type.statuses
162
+ ]:
163
+ logging.error(
164
+ f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
165
+ )
166
+ error |= ValidationError.INVALID_ISSUE_STATE
167
+
168
+ return error
169
+
170
+
171
+ def _validate_issue_fields(
172
+ jira: JiraClient, board: JiraBoardV1, project_issue_type: IssueType
173
+ ) -> tuple[ValidationError, CustomIssueFields]:
174
+ """Validate that custom fields exist and have valid values."""
175
+ error = NO_ERRORS
176
+ custom_fields: CustomIssueFields = {}
177
+
178
+ for field in board.issue_fields or []:
179
+ project_issue_field = jira.project_issue_field(
180
+ issue_type_id=project_issue_type.id, field=field.name
181
+ )
182
+ if not project_issue_field:
183
+ logging.error(
184
+ f"[{board.name}] '{field.name}' is not a valid field for '{project_issue_type.name}' in this project."
185
+ )
186
+ error |= ValidationError.INVALID_ISSUE_FIELD
187
+ continue
188
+
189
+ for option in project_issue_field.options:
190
+ if option == field.value:
191
+ custom_fields[project_issue_field.id] = option.model_dump()
192
+ break
193
+ else:
194
+ logging.error(
195
+ f"[{board.name}] '{field.name}' has an invalid value '{field.value}'. Valid values: {project_issue_field.options}"
196
+ )
197
+ error |= ValidationError.INVALID_ISSUE_FIELD
198
+
199
+ return error, custom_fields
200
+
201
+
202
+ def _validate_security_level(
203
+ board: JiraBoardV1, public_projects: Iterable[str]
204
+ ) -> ValidationError:
205
+ """Validate that public projects have a security level defined."""
206
+ if board.name in public_projects and "Security Level" not in [
207
+ f.name for f in board.issue_fields or []
208
+ ]:
209
+ logging.error(
210
+ f"[{board.name}] is a public project, but the security level is not defined. Please add 'Security Level' field to the issueFields!"
211
+ )
212
+ return ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
213
+ return NO_ERRORS
214
+
215
+
216
+ def _validate_priorities(
217
+ jira: JiraClient, board: JiraBoardV1, jira_server_priorities: NameToIdMap
218
+ ) -> ValidationError:
219
+ """Validate that all priority mappings are valid for the project."""
220
+ error = NO_ERRORS
221
+ project_priorities = jira.project_priority_scheme()
222
+ project_priorities_names = [
223
+ p_name
224
+ for project_p_id in project_priorities
225
+ for p_name, p_id in jira_server_priorities.items()
226
+ if p_id == project_p_id
227
+ ]
228
+
229
+ for priority in board.severity_priority_mappings.mappings:
230
+ if priority.priority not in jira_server_priorities:
231
+ logging.error(
232
+ f"[{board.name}] {priority.priority} is not a valid Jira priority. Valid priorities: {project_priorities_names}"
233
+ )
234
+ error |= ValidationError.INVALID_PRIORITY
235
+ continue
236
+ if (
237
+ project_priorities
238
+ and jira_server_priorities[priority.priority] not in project_priorities
239
+ ):
240
+ logging.error(
241
+ f"[{board.name}] {priority.priority} is not a valid priority in project. Valid priorities: {project_priorities_names}"
242
+ )
243
+ error |= ValidationError.INVALID_PRIORITY
244
+
245
+ return error
246
+
247
+
80
248
  def board_is_valid(
81
249
  jira: JiraClient,
82
250
  board: JiraBoardV1,
@@ -85,123 +253,52 @@ def board_is_valid(
85
253
  jira_server_priorities: NameToIdMap,
86
254
  public_projects: Iterable[str],
87
255
  ) -> tuple[ValidationError, CustomIssueFields]:
88
- error = ValidationError(0)
256
+ """Validate all Jira board settings.
257
+
258
+ This method orchestrates multiple validation checks for a Jira board configuration.
259
+ Each validation is performed by a dedicated helper function that focuses on a specific
260
+ aspect of the board configuration.
261
+
262
+ Returns:
263
+ A tuple of (ValidationError flags, custom_fields dict)
264
+ """
265
+ error = NO_ERRORS
89
266
  custom_fields: CustomIssueFields = {}
90
267
 
91
268
  try:
92
- if jira.is_archived:
93
- logging.error(f"[{board.name}] project is archived")
94
- return ValidationError.PROJECT_ARCHIVED, custom_fields
269
+ # Check if project is archived (early exit)
270
+ archived_error = _validate_project_archived(jira, board)
271
+ if archived_error:
272
+ return archived_error, custom_fields
95
273
 
96
- if not jira.can_create_issues():
97
- logging.error(f"[{board.name}] can not create issues in project")
98
- error |= ValidationError.CANT_CREATE_ISSUE
274
+ # Validate permissions
275
+ error |= _validate_permissions(jira, board)
99
276
 
100
- if not jira.can_transition_issues():
101
- logging.error(
102
- f"[{board.name}] AppSRE Jira Bot user does not have the permission to change the issue status."
103
- )
104
- error |= ValidationError.CANT_TRANSITION_ISSUES
105
-
106
- components = jira.components()
107
- for escalation_policy in board.escalation_policies or []:
108
- for jira_component in escalation_policy.channels.jira_components or []:
109
- if jira_component not in components:
110
- logging.error(
111
- f"[{board.name}] escalation policy '{escalation_policy.name}' references a non existing Jira component "
112
- f"'{jira_component}'. Valid components: {components}"
113
- )
114
- error |= ValidationError.INVALID_COMPONENT
277
+ # Validate components
278
+ error |= _validate_components(jira, board)
115
279
 
116
- issue_type = board.issue_type or default_issue_type
117
- project_issue_type = jira.get_issue_type(issue_type)
118
- if not project_issue_type:
119
- project_issue_types_str = ", ".join(
120
- t.name for t in jira.project_issue_types()
121
- )
122
- logging.error(
123
- f"[{board.name}] '{issue_type}' is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
124
- )
125
- error |= ValidationError.INVALID_ISSUE_TYPE
280
+ # Validate issue type
281
+ issue_type_error, project_issue_type = _validate_issue_type(
282
+ jira, board, default_issue_type
283
+ )
284
+ error |= issue_type_error
126
285
 
286
+ # If issue type is valid, perform additional validations
127
287
  if project_issue_type:
128
- # Check issue attributes
129
- if not project_issue_type.statuses:
130
- logging.error(
131
- f"[{board.name}] '{issue_type}' doesn't have any status. Choose a different issue type."
132
- )
133
- error |= ValidationError.INVALID_ISSUE_TYPE
134
-
135
- reopen_state = board.issue_reopen_state or default_reopen_state
136
- if reopen_state.lower() not in [
137
- t.lower() for t in project_issue_type.statuses
138
- ]:
139
- logging.error(
140
- f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
141
- )
142
- error |= ValidationError.INVALID_ISSUE_STATE
143
-
144
- if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
145
- t.lower() for t in project_issue_type.statuses
146
- ]:
147
- logging.error(
148
- f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
149
- )
150
- error |= ValidationError.INVALID_ISSUE_STATE
288
+ error |= _validate_issue_states(
289
+ board, project_issue_type, default_reopen_state
290
+ )
291
+ fields_error, custom_fields = _validate_issue_fields(
292
+ jira, board, project_issue_type
293
+ )
294
+ error |= fields_error
151
295
 
152
- for field in board.issue_fields or []:
153
- project_issue_field = jira.project_issue_field(
154
- issue_type_id=project_issue_type.id, field=field.name
155
- )
156
- if not project_issue_field:
157
- logging.error(
158
- f"[{board.name}] '{field.name}' is not a valid field for '{project_issue_type.name}' in this project."
159
- )
296
+ # Validate security level for public projects
297
+ error |= _validate_security_level(board, public_projects)
160
298
 
161
- error |= ValidationError.INVALID_ISSUE_FIELD
162
- continue
299
+ # Validate priorities
300
+ error |= _validate_priorities(jira, board, jira_server_priorities)
163
301
 
164
- for option in project_issue_field.options:
165
- if option == field.value:
166
- # cache the field id and option value/name for later use, e.g. in jiralert templates
167
- custom_fields[project_issue_field.id] = option.dict()
168
- break
169
- else:
170
- # field.value is not in the allowed options
171
- logging.error(
172
- f"[{board.name}] '{field.name}' has an invalid value '{field.value}'. Valid values: {project_issue_field.options}"
173
- )
174
- error |= ValidationError.INVALID_ISSUE_FIELD
175
- continue
176
-
177
- if board.name in public_projects and "Security Level" not in [
178
- f.name for f in board.issue_fields or []
179
- ]:
180
- logging.error(
181
- f"[{board.name}] is a public project, but the security level is not defined. Please add 'Security Level' field to the issueFields!"
182
- )
183
- error |= ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
184
-
185
- project_priorities = jira.project_priority_scheme()
186
- # get the priority names from the project priorities ids
187
- project_priorities_names = [
188
- p_name
189
- for project_p_id in project_priorities
190
- for p_name, p_id in jira_server_priorities.items()
191
- if p_id == project_p_id
192
- ]
193
- for priority in board.severity_priority_mappings.mappings:
194
- if priority.priority not in jira_server_priorities:
195
- logging.error(
196
- f"[{board.name}] {priority.priority} is not a valid Jira priority. Valid priorities: {project_priorities_names}"
197
- )
198
- error |= ValidationError.INVALID_PRIORITY
199
- continue
200
- if jira_server_priorities[priority.priority] not in project_priorities:
201
- logging.error(
202
- f"[{board.name}] {priority.priority} is not a valid priority in project. Valid priorities: {project_priorities_names}"
203
- )
204
- error |= ValidationError.INVALID_PRIORITY
205
302
  except JIRAError as e:
206
303
  if e.status_code == 401:
207
304
  # sporadic 401 errors, retrying
@@ -228,25 +325,41 @@ def validate_boards(
228
325
  dry_run: bool,
229
326
  state: State,
230
327
  jira_client_class: type[JiraClient] = JiraClient,
328
+ use_cache: bool = False,
231
329
  ) -> bool:
330
+ """Validate all Jira boards.
331
+
332
+ The method iterates over all Jira boards and checks if the configuration is valid. If no errors
333
+ are found, it will skip the next check for the board until the next run time is reached.
334
+ The next run time is calculated based on the board's check interval and some randomness to avoid
335
+ all boards checking at the same time.
336
+
337
+ Additionally, for any Jira board with a permission error, a Prometheus metric will be set to trigger an alert.
338
+
339
+ Returns True if there were any errors. See ValidationError and log messages for details.
340
+ """
232
341
  error = False
233
342
  jira_clients: dict[str, JiraClient] = {}
234
343
  for board in jira_boards:
235
- next_run_time = state.get(board.name, 0)
236
- if time.time() <= next_run_time:
237
- if not dry_run:
238
- # always skip for non-dry-run mode
239
- continue
240
- # dry-run mode
241
- elif len(jira_boards) > 1:
242
- logging.info(f"[{board.name}] Use cache results. Skipping ...")
243
- continue
344
+ if use_cache:
345
+ next_run_time = state.get(board.name, 0)
346
+ if time.time() <= next_run_time:
347
+ if not dry_run:
348
+ # always skip for non-dry-run mode
349
+ continue
350
+ # dry-run mode
351
+ elif len(jira_boards) > 1:
352
+ logging.info(f"[{board.name}] Use cache results. Skipping ...")
353
+ continue
244
354
 
245
355
  logging.debug(f"[{board.name}] checking ...")
246
356
  if board.server.server_url not in jira_clients:
247
357
  jira_clients[board.server.server_url] = jira_client_class.create(
248
358
  project_name=board.name,
249
359
  token=secret_reader.read_secret(board.server.token),
360
+ email=secret_reader.read_secret(board.server.email)
361
+ if board.server.email
362
+ else None,
250
363
  server_url=board.server.server_url,
251
364
  jira_watcher_settings=jira_client_settings,
252
365
  )
@@ -318,7 +431,7 @@ def get_jira_boards(
318
431
 
319
432
 
320
433
  def export_boards(boards: list[JiraBoardV1]) -> list[dict]:
321
- return [board.dict() for board in boards]
434
+ return [board.model_dump() for board in boards]
322
435
 
323
436
 
324
437
  @defer
@@ -326,6 +439,7 @@ def run(
326
439
  dry_run: bool,
327
440
  jira_board_name: list[str] | None = None,
328
441
  board_check_interval_sec: int = 3600,
442
+ use_cache: bool = False,
329
443
  defer: Callable | None = None,
330
444
  ) -> None:
331
445
  gql_api = gql.get_api()
@@ -349,6 +463,7 @@ def run(
349
463
  board_check_interval_sec=board_check_interval_sec,
350
464
  dry_run=dry_run,
351
465
  state=state,
466
+ use_cache=use_cache,
352
467
  )
353
468
 
354
469
  if error:
@@ -58,7 +58,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
58
58
  """Return the desired state for early exit."""
59
59
  if not query_func:
60
60
  query_func = gql.get_api().query
61
- return {"roles": [c.dict() for c in self.get_roles(query_func)]}
61
+ return {"roles": [c.model_dump() for c in self.get_roles(query_func)]}
62
62
 
63
63
  @defer
64
64
  def run(self, dry_run: bool, defer: Callable | None = None) -> None:
reconcile/ocm/types.py CHANGED
@@ -1,10 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from pydantic import (
4
- BaseModel,
5
- Extra,
6
- Field,
7
- )
3
+ from pydantic import BaseModel, Field, field_validator
8
4
 
9
5
 
10
6
  class OCMClusterAutoscale(BaseModel):
@@ -13,99 +9,82 @@ class OCMClusterAutoscale(BaseModel):
13
9
 
14
10
 
15
11
  class OCMClusterNetwork(BaseModel):
16
- type: str | None
12
+ type: str | None = None
17
13
  vpc: str
18
14
  service: str
19
15
  pod: str
20
16
 
21
17
 
22
- class OCMClusterSpec(BaseModel):
23
- autoscale: OCMClusterAutoscale | None
18
+ class OCMClusterSpec(BaseModel, extra="forbid"):
19
+ autoscale: OCMClusterAutoscale | None = None
24
20
  channel: str
25
- disable_user_workload_monitoring: bool | None
26
- external_id: str | None
27
- id: str | None
28
- instance_type: str | None
29
- multi_az: bool | None
30
- nodes: int | None
21
+ disable_user_workload_monitoring: bool | None = None
22
+ external_id: str | None = None
23
+ id: str | None = None
24
+ instance_type: str | None = None
25
+ multi_az: bool | None = None
26
+ nodes: int | None = None
31
27
  private: bool
32
28
  product: str
33
29
  provider: str
34
- provision_shard_id: str | None
30
+ provision_shard_id: str | None = None
35
31
  region: str
36
- initial_version: str | None
32
+ initial_version: str | None = None
37
33
  version: str
38
- hypershift: bool | None
34
+ hypershift: bool | None = None
35
+ fips: bool = False
39
36
 
40
- class Config:
41
- extra = Extra.forbid
37
+ @field_validator("fips", mode="before")
38
+ @classmethod
39
+ def set_fips_default(cls, v: bool | None) -> bool:
40
+ return v or False
42
41
 
43
42
 
44
- class OSDClusterSpec(OCMClusterSpec):
43
+ class OSDClusterSpec(OCMClusterSpec, extra="forbid"):
45
44
  load_balancers: int
46
45
  storage: int
47
46
 
48
- class Config:
49
- extra = Extra.forbid
50
47
 
51
-
52
- class ROSAOcmAwsStsAttrs(BaseModel):
48
+ class ROSAOcmAwsStsAttrs(BaseModel, extra="forbid"):
53
49
  installer_role_arn: str
54
50
  support_role_arn: str
55
- controlplane_role_arn: str | None
51
+ controlplane_role_arn: str | None = None
56
52
  worker_role_arn: str
57
53
 
58
- class Config:
59
- extra = Extra.forbid
60
-
61
54
 
62
- class ROSAOcmAwsAttrs(BaseModel):
55
+ class ROSAOcmAwsAttrs(BaseModel, extra="forbid"):
63
56
  creator_role_arn: str
64
- sts: ROSAOcmAwsStsAttrs | None
65
-
66
- class Config:
67
- extra = Extra.forbid
57
+ sts: ROSAOcmAwsStsAttrs | None = None
68
58
 
69
59
 
70
- class ROSAClusterAWSAccount(BaseModel):
60
+ class ROSAClusterAWSAccount(BaseModel, extra="forbid"):
71
61
  uid: str
72
- rosa: ROSAOcmAwsAttrs | None
73
- billing_account_id: str | None
62
+ rosa: ROSAOcmAwsAttrs | None = None
63
+ billing_account_id: str | None = None
74
64
 
75
- class Config:
76
- extra = Extra.forbid
77
65
 
78
-
79
- class ROSAClusterSpec(OCMClusterSpec):
66
+ class ROSAClusterSpec(OCMClusterSpec, extra="forbid"):
80
67
  account: ROSAClusterAWSAccount
81
- subnet_ids: list[str] | None
82
- availability_zones: list[str] | None
83
- oidc_endpoint_url: str | None
84
-
85
- class Config:
86
- extra = Extra.forbid
68
+ subnet_ids: list[str] | None = None
69
+ availability_zones: list[str] | None = None
70
+ oidc_endpoint_url: str | None = None
87
71
 
88
72
 
89
73
  class ClusterMachinePool(BaseModel):
90
74
  id: str
91
75
  instance_type: str
92
- replicas: int | None
93
- autoscale: OCMClusterAutoscale | None
76
+ replicas: int | None = None
77
+ autoscale: OCMClusterAutoscale | None = None
94
78
 
95
79
 
96
- class OCMSpec(BaseModel):
97
- path: str | None
80
+ class OCMSpec(BaseModel, validate_by_name=True, validate_by_alias=True):
81
+ path: str | None = None
98
82
  spec: OSDClusterSpec | ROSAClusterSpec | OCMClusterSpec
99
83
  machine_pools: list[ClusterMachinePool] = Field(
100
84
  default_factory=list, alias="machinePools"
101
85
  )
102
86
  network: OCMClusterNetwork
103
- domain: str | None
87
+ domain: str | None = None
104
88
  server_url: str = Field("", alias="serverUrl")
105
89
  console_url: str = Field("", alias="consoleUrl")
106
90
  elb_fqdn: str = Field("", alias="elbFQDN")
107
-
108
- class Config:
109
- smart_union = True
110
- # This is need to populate by either console_url or consoleUrl, for instance
111
- allow_population_by_field_name = True
@@ -103,7 +103,7 @@ def fetch_desired_state(clusters: Iterable[Mapping[str, Any]]) -> list[dict[str,
103
103
  namespaces = get_namespaces()
104
104
  for namespace_info in namespaces:
105
105
  specs = get_external_resource_specs(
106
- namespace_info.dict(by_alias=True), provision_provider=PROVIDER_AWS
106
+ namespace_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
107
107
  )
108
108
  for spec in specs:
109
109
  if spec.provider != "aws-iam-service-account":
reconcile/ocm_clusters.py CHANGED
@@ -248,13 +248,13 @@ def get_cluster_ocm_update_spec(
248
248
  if not desired_spec.network.type:
249
249
  desired_spec.network.type = "OVNKubernetes"
250
250
 
251
- cspec = current_spec.spec.dict()
252
- cspec[ocmmod.SPEC_ATTR_NETWORK] = current_spec.network.dict(
251
+ cspec = current_spec.spec.model_dump()
252
+ cspec[ocmmod.SPEC_ATTR_NETWORK] = current_spec.network.model_dump(
253
253
  exclude={IGNORE_NETWORK_TYPE_ATTR}
254
254
  )
255
255
 
256
- dspec = desired_spec.spec.dict()
257
- dspec[ocmmod.SPEC_ATTR_NETWORK] = desired_spec.network.dict(
256
+ dspec = desired_spec.spec.model_dump()
257
+ dspec[ocmmod.SPEC_ATTR_NETWORK] = desired_spec.network.model_dump(
258
258
  exclude={IGNORE_NETWORK_TYPE_ATTR}
259
259
  )
260
260
 
@@ -4,7 +4,7 @@ import logging
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from deepdiff import DeepHash
7
- from pydantic import validator
7
+ from pydantic import field_validator
8
8
 
9
9
  from reconcile.aus.aus_label_source import (
10
10
  init_aus_cluster_label_source,
@@ -62,7 +62,8 @@ class OcmLabelsIntegrationParams(PydanticRunParams):
62
62
  managed_label_prefixes: list[str] = []
63
63
  ignored_label_prefixes: list[str] = []
64
64
 
65
- @validator("managed_label_prefixes", "ignored_label_prefixes")
65
+ @field_validator("managed_label_prefixes", "ignored_label_prefixes")
66
+ @classmethod
66
67
  def must_end_with_dot(cls, v: list[str]) -> list[str]:
67
68
  return [prefix + "." if not prefix.endswith(".") else prefix for prefix in v]
68
69