qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev474__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 (371) hide show
  1. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/METADATA +14 -13
  2. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/RECORD +371 -364
  3. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/WHEEL +1 -1
  4. reconcile/acs_rbac.py +2 -2
  5. reconcile/aus/advanced_upgrade_service.py +18 -12
  6. reconcile/aus/aus_sts_gate_handler.py +59 -0
  7. reconcile/aus/base.py +137 -34
  8. reconcile/aus/cluster_version_data.py +15 -5
  9. reconcile/aus/models.py +3 -1
  10. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  11. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  12. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  13. reconcile/aus/version_gate_approver.py +1 -16
  14. reconcile/aus/version_gates/sts_version_gate_handler.py +5 -72
  15. reconcile/automated_actions/config/integration.py +16 -4
  16. reconcile/aws_account_manager/integration.py +21 -9
  17. reconcile/aws_account_manager/reconciler.py +3 -3
  18. reconcile/aws_account_manager/utils.py +1 -1
  19. reconcile/aws_ami_cleanup/integration.py +8 -12
  20. reconcile/aws_ami_share.py +69 -62
  21. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  22. reconcile/aws_ecr_image_pull_secrets.py +1 -1
  23. reconcile/aws_iam_keys.py +1 -0
  24. reconcile/aws_saml_idp/integration.py +12 -4
  25. reconcile/aws_saml_roles/integration.py +30 -23
  26. reconcile/aws_version_sync/integration.py +6 -12
  27. reconcile/change_owners/README.md +1 -1
  28. reconcile/change_owners/bundle.py +3 -3
  29. reconcile/change_owners/change_log_tracking.py +3 -2
  30. reconcile/change_owners/change_owners.py +108 -42
  31. reconcile/change_owners/decision.py +1 -1
  32. reconcile/change_owners/diff.py +0 -2
  33. reconcile/checkpoint.py +11 -3
  34. reconcile/cli.py +94 -11
  35. reconcile/dashdotdb_dora.py +5 -12
  36. reconcile/dashdotdb_slo.py +1 -1
  37. reconcile/database_access_manager.py +123 -117
  38. reconcile/dynatrace_token_provider/integration.py +1 -1
  39. reconcile/endpoints_discovery/integration.py +4 -1
  40. reconcile/endpoints_discovery/merge_request.py +1 -1
  41. reconcile/endpoints_discovery/merge_request_manager.py +8 -8
  42. reconcile/external_resources/factories.py +4 -6
  43. reconcile/external_resources/integration.py +1 -1
  44. reconcile/external_resources/manager.py +8 -6
  45. reconcile/external_resources/meta.py +0 -1
  46. reconcile/external_resources/metrics.py +1 -1
  47. reconcile/external_resources/model.py +19 -15
  48. reconcile/external_resources/reconciler.py +7 -4
  49. reconcile/external_resources/secrets_sync.py +6 -10
  50. reconcile/external_resources/state.py +26 -16
  51. reconcile/fleet_labeler/integration.py +1 -1
  52. reconcile/gabi_authorized_users.py +5 -2
  53. reconcile/gcp_image_mirror.py +2 -2
  54. reconcile/github_org.py +1 -1
  55. reconcile/github_owners.py +4 -0
  56. reconcile/gitlab_housekeeping.py +13 -15
  57. reconcile/gitlab_members.py +6 -12
  58. reconcile/gitlab_owners.py +15 -11
  59. reconcile/gitlab_permissions.py +8 -12
  60. reconcile/glitchtip_project_alerts/integration.py +3 -1
  61. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  62. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  63. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  64. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  65. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  66. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  67. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  68. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  69. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  70. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +14 -5
  71. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  72. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  73. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  74. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  75. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  76. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  77. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  78. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  79. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  80. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  81. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  84. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  85. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  87. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  88. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  89. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  90. reconcile/gql_definitions/common/apps.py +5 -5
  91. reconcile/gql_definitions/common/aws_vpc_requests.py +18 -5
  92. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  93. reconcile/gql_definitions/common/clusters.py +7 -5
  94. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  95. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  96. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  97. reconcile/gql_definitions/common/github_orgs.py +5 -5
  98. reconcile/gql_definitions/common/jira_settings.py +5 -5
  99. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  100. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  101. reconcile/gql_definitions/common/namespaces.py +5 -5
  102. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  103. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  104. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  105. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  106. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  107. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  108. reconcile/gql_definitions/common/quay_instances.py +5 -5
  109. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  110. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  111. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  112. reconcile/gql_definitions/common/saas_files.py +5 -5
  113. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  114. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  115. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  116. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  117. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  118. reconcile/gql_definitions/common/users.py +5 -5
  119. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  120. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  121. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  122. reconcile/gql_definitions/cost_report/settings.py +5 -5
  123. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  124. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  125. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  126. reconcile/gql_definitions/email_sender/apps.py +5 -5
  127. reconcile/gql_definitions/email_sender/emails.py +5 -5
  128. reconcile/gql_definitions/email_sender/users.py +5 -5
  129. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  130. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  131. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  132. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +38 -7
  133. reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
  134. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  135. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  136. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  138. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  139. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  141. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  142. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  143. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  144. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  145. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  146. reconcile/gql_definitions/fragments/disable.py +5 -5
  147. reconcile/gql_definitions/fragments/email_service.py +5 -5
  148. reconcile/gql_definitions/fragments/email_user.py +5 -5
  149. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  150. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  151. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  152. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  153. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  154. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  155. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  156. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  157. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  158. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  159. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  160. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  161. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  162. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  163. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  164. reconcile/gql_definitions/fragments/user.py +5 -5
  165. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  166. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  167. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  168. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  169. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  170. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  171. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  172. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  173. reconcile/gql_definitions/integrations/integrations.py +5 -5
  174. reconcile/gql_definitions/introspection.json +775 -136
  175. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  176. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  177. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  178. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  179. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  180. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  181. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  182. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  183. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  184. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  185. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  186. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  187. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  188. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  189. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  190. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  191. reconcile/gql_definitions/rhcs/certs.py +25 -79
  192. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  193. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  194. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  195. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  196. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  197. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  198. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  199. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  200. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  201. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  202. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  203. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  204. reconcile/gql_definitions/status_board/status_board.py +5 -5
  205. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  206. reconcile/gql_definitions/templating/template_collection.py +5 -5
  207. reconcile/gql_definitions/templating/templates.py +5 -5
  208. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  209. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  210. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  211. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  212. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  213. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  214. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  215. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  216. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  217. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +37 -7
  218. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  219. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  220. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  221. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  222. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -5
  223. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +6 -5
  224. reconcile/integrations_manager.py +3 -3
  225. reconcile/jenkins_worker_fleets.py +10 -8
  226. reconcile/jira_permissions_validator.py +237 -122
  227. reconcile/ldap_groups/integration.py +1 -1
  228. reconcile/ocm/types.py +35 -57
  229. reconcile/ocm_aws_infrastructure_access.py +1 -1
  230. reconcile/ocm_clusters.py +4 -4
  231. reconcile/ocm_labels/integration.py +3 -2
  232. reconcile/ocm_machine_pools.py +33 -27
  233. reconcile/openshift_base.py +113 -4
  234. reconcile/openshift_cluster_bots.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +96 -101
  237. reconcile/openshift_resources_base.py +6 -2
  238. reconcile/openshift_rhcs_certs.py +74 -37
  239. reconcile/openshift_rolebindings.py +7 -11
  240. reconcile/openshift_saas_deploy.py +4 -5
  241. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  242. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  243. reconcile/openshift_serviceaccount_tokens.py +2 -2
  244. reconcile/openshift_upgrade_watcher.py +4 -4
  245. reconcile/oum/labelset.py +5 -3
  246. reconcile/oum/models.py +1 -4
  247. reconcile/prometheus_rules_tester/integration.py +3 -3
  248. reconcile/quay_base.py +25 -6
  249. reconcile/quay_membership.py +55 -29
  250. reconcile/quay_mirror.py +1 -1
  251. reconcile/quay_mirror_org.py +6 -4
  252. reconcile/quay_permissions.py +81 -75
  253. reconcile/quay_repos.py +35 -37
  254. reconcile/queries.py +132 -1
  255. reconcile/rhidp/common.py +3 -5
  256. reconcile/rhidp/sso_client/base.py +16 -5
  257. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  258. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  259. reconcile/skupper_network/integration.py +2 -2
  260. reconcile/slack_usergroups.py +35 -14
  261. reconcile/sql_query.py +1 -0
  262. reconcile/status_board.py +6 -6
  263. reconcile/statuspage/atlassian.py +7 -7
  264. reconcile/statuspage/integrations/maintenances.py +4 -3
  265. reconcile/statuspage/page.py +4 -9
  266. reconcile/statuspage/status.py +5 -8
  267. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +1 -1
  268. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +1 -1
  269. reconcile/templating/lib/rendering.py +3 -3
  270. reconcile/templating/renderer.py +2 -2
  271. reconcile/templating/validator.py +4 -4
  272. reconcile/terraform_aws_route53.py +7 -1
  273. reconcile/terraform_cloudflare_dns.py +3 -3
  274. reconcile/terraform_cloudflare_resources.py +5 -5
  275. reconcile/terraform_cloudflare_users.py +3 -2
  276. reconcile/terraform_init/integration.py +187 -23
  277. reconcile/terraform_repo.py +16 -12
  278. reconcile/terraform_resources.py +6 -6
  279. reconcile/terraform_tgw_attachments.py +27 -19
  280. reconcile/terraform_users.py +7 -0
  281. reconcile/terraform_vpc_peerings.py +14 -3
  282. reconcile/terraform_vpc_resources/integration.py +20 -8
  283. reconcile/terraform_vpc_resources/merge_request.py +12 -2
  284. reconcile/terraform_vpc_resources/merge_request_manager.py +43 -19
  285. reconcile/typed_queries/aws_account_tags.py +41 -0
  286. reconcile/typed_queries/cost_report/app_names.py +1 -1
  287. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  288. reconcile/typed_queries/saas_files.py +20 -15
  289. reconcile/typed_queries/status_board.py +2 -2
  290. reconcile/unleash_feature_toggles/integration.py +4 -2
  291. reconcile/utils/acs/base.py +6 -3
  292. reconcile/utils/acs/policies.py +2 -2
  293. reconcile/utils/aws_api.py +51 -20
  294. reconcile/utils/aws_api_typed/api.py +38 -9
  295. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  296. reconcile/utils/aws_api_typed/logs.py +73 -0
  297. reconcile/utils/aws_api_typed/organization.py +4 -2
  298. reconcile/utils/binary.py +7 -12
  299. reconcile/utils/datetime_util.py +67 -0
  300. reconcile/utils/deadmanssnitch_api.py +1 -1
  301. reconcile/utils/differ.py +2 -3
  302. reconcile/utils/early_exit_cache.py +11 -12
  303. reconcile/utils/environ.py +5 -0
  304. reconcile/utils/expiration.py +7 -3
  305. reconcile/utils/external_resource_spec.py +2 -0
  306. reconcile/utils/filtering.py +1 -1
  307. reconcile/utils/gitlab_api.py +19 -5
  308. reconcile/utils/glitchtip/client.py +6 -2
  309. reconcile/utils/glitchtip/models.py +25 -28
  310. reconcile/utils/gql.py +4 -7
  311. reconcile/utils/helpers.py +1 -1
  312. reconcile/utils/instrumented_wrappers.py +1 -1
  313. reconcile/utils/internal_groups/client.py +2 -2
  314. reconcile/utils/internal_groups/models.py +8 -17
  315. reconcile/utils/jinja2/utils.py +6 -101
  316. reconcile/utils/jira_client.py +82 -63
  317. reconcile/utils/jjb_client.py +26 -13
  318. reconcile/utils/jobcontroller/controller.py +2 -2
  319. reconcile/utils/jobcontroller/models.py +17 -1
  320. reconcile/utils/json.py +43 -1
  321. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  322. reconcile/utils/membershipsources/models.py +16 -23
  323. reconcile/utils/membershipsources/resolver.py +4 -2
  324. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  325. reconcile/utils/merge_request_manager/parser.py +6 -6
  326. reconcile/utils/metrics.py +5 -5
  327. reconcile/utils/models.py +304 -82
  328. reconcile/utils/mr/app_interface_reporter.py +2 -2
  329. reconcile/utils/mr/notificator.py +3 -3
  330. reconcile/utils/mr/update_access_report_base.py +3 -4
  331. reconcile/utils/mr/user_maintenance.py +3 -2
  332. reconcile/utils/oc.py +252 -201
  333. reconcile/utils/oc_filters.py +3 -3
  334. reconcile/utils/ocm/addons.py +0 -1
  335. reconcile/utils/ocm/base.py +17 -20
  336. reconcile/utils/ocm/cluster_groups.py +1 -1
  337. reconcile/utils/ocm/identity_providers.py +2 -2
  338. reconcile/utils/ocm/labels.py +1 -1
  339. reconcile/utils/ocm/products.py +8 -8
  340. reconcile/utils/ocm/search_filters.py +3 -6
  341. reconcile/utils/ocm/service_log.py +4 -6
  342. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  343. reconcile/utils/openshift_resource.py +8 -3
  344. reconcile/utils/pagerduty_api.py +10 -7
  345. reconcile/utils/promotion_state.py +6 -11
  346. reconcile/utils/quay_api.py +74 -87
  347. reconcile/utils/raw_github_api.py +1 -1
  348. reconcile/utils/rhcsv2_certs.py +86 -23
  349. reconcile/utils/rosa/session.py +16 -0
  350. reconcile/utils/runtime/integration.py +2 -3
  351. reconcile/utils/runtime/runner.py +2 -2
  352. reconcile/utils/saasherder/interfaces.py +13 -20
  353. reconcile/utils/saasherder/models.py +23 -20
  354. reconcile/utils/saasherder/saasherder.py +50 -27
  355. reconcile/utils/slack_api.py +2 -2
  356. reconcile/utils/sloth.py +171 -2
  357. reconcile/utils/structs.py +1 -1
  358. reconcile/utils/terraform_client.py +5 -4
  359. reconcile/utils/terrascript_aws_client.py +274 -124
  360. reconcile/utils/unleash/server.py +2 -8
  361. reconcile/utils/vault.py +5 -12
  362. reconcile/utils/vcs.py +8 -8
  363. reconcile/vault_replication.py +107 -42
  364. reconcile/vpc_peerings_validator.py +13 -0
  365. tools/app_interface_reporter.py +4 -4
  366. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  367. tools/cli_commands/cost_report/view.py +7 -6
  368. tools/cli_commands/erv2.py +1 -1
  369. tools/qontract_cli.py +28 -17
  370. tools/template_validation.py +3 -1
  371. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/entry_points.txt +0 -0
reconcile/utils/models.py CHANGED
@@ -1,7 +1,5 @@
1
1
  from collections import UserList
2
2
  from collections.abc import (
3
- Callable,
4
- Generator,
5
3
  MutableMapping,
6
4
  )
7
5
  from typing import Any
@@ -9,75 +7,311 @@ from typing import Any
9
7
  from croniter import croniter
10
8
  from pydantic import (
11
9
  BaseModel,
10
+ GetCoreSchemaHandler,
12
11
  ValidationError,
13
12
  )
14
- from pydantic import errors as pydantic_errors
15
- from pydantic.fields import ModelField
13
+ from pydantic_core import core_schema
16
14
 
17
15
  DEFAULT_STRING = "I was too lazy to define a string here"
18
16
  DEFAULT_INT = 42
17
+ DEFAULT_BOOL = False
18
+ DEFAULT_JSON_STR = "{}"
19
+ DEFAULT_JSON: dict = {}
20
+
21
+ DataType = MutableMapping[str, Any] | str | int | bool | list | None
22
+
23
+
24
+ def _process_field_with_alias(
25
+ field_info: dict[str, Any],
26
+ data: DataType,
27
+ alias: str | None,
28
+ use_defaults: bool,
29
+ definitions: list[dict[str, Any]] | None,
30
+ ) -> DataType:
31
+ """Process a field by recursively calling data_default_none and handling alias.
32
+
33
+ This helper function encapsulates the common pattern used by 'default',
34
+ 'nullable', and 'model' field types where we need to:
35
+ 1. Recursively process the field's schema
36
+ 2. Handle data extraction via alias if data is a dict
37
+ 3. Update the data dict with the processed value if alias exists
38
+
39
+ Args:
40
+ field_info: Schema information for the field to process
41
+ data: Input data (can be dict, primitive, list, or None)
42
+ alias: Field alias for dict key access, or None for unnamed fields
43
+ use_defaults: Whether to use default values for missing required fields
44
+ definitions: Schema definitions for definition-ref fields
45
+
46
+ Returns:
47
+ Processed data value, or updated data dict if alias was provided
48
+ """
49
+ # Recursively process the field's schema
50
+ value = data_default_none(
51
+ field_info["schema"],
52
+ data.get(alias) if isinstance(data, MutableMapping) and alias else data,
53
+ use_defaults=use_defaults,
54
+ definitions=definitions,
55
+ )
56
+
57
+ # For unnamed fields (in recursive calls), return value directly
58
+ if not alias:
59
+ return value
60
+
61
+ # For named fields, update the data dict
62
+ if not isinstance(data, MutableMapping):
63
+ raise TypeError(
64
+ f"Expected MutableMapping for field '{alias}', got {type(data).__name__}"
65
+ )
66
+ data[alias] = value
67
+ return data
19
68
 
20
69
 
21
70
  def data_default_none(
22
- klass: type[BaseModel], data: MutableMapping[str, Any], use_defaults: bool = True
23
- ) -> MutableMapping[str, Any]:
24
- """Set default values to None for required but optional fields."""
25
- for field in klass.__fields__.values():
26
- if not field.required:
27
- continue
28
-
29
- if field.alias not in data:
30
- # Settings defaults
31
- if field.allow_none:
32
- data[field.alias] = None
33
- elif not use_defaults:
34
- raise ValueError(f"Field {field.alias} is required but not set.")
35
- elif isinstance(field.type_, type) and issubclass(field.type_, str):
36
- data[field.alias] = DEFAULT_STRING
37
- elif isinstance(field.type_, type) and issubclass(field.type_, bool):
38
- data[field.alias] = False
39
- elif isinstance(field.type_, type) and issubclass(field.type_, int):
40
- data[field.alias] = DEFAULT_INT
41
- elif isinstance(field.type_, type) and issubclass(field.type_, BaseModel):
42
- if isinstance(data[field.alias], dict):
43
- data[field.alias] = data_default_none(field.type_, data[field.alias])
44
- if isinstance(data[field.alias], list):
45
- data[field.alias] = [
46
- data_default_none(field.type_, item)
47
- for item in data[field.alias]
48
- if isinstance(item, dict)
71
+ klass: type[BaseModel] | dict,
72
+ data: DataType,
73
+ use_defaults: bool = True,
74
+ definitions: list[dict[str, Any]] | None = None,
75
+ ) -> DataType:
76
+ """Set default values for required fields in Pydantic model data.
77
+
78
+ This function recursively processes Pydantic model schemas and fills in
79
+ missing fields with appropriate default values based on their type. It's
80
+ primarily used to handle GraphQL query results where fields may be missing.
81
+
82
+ Args:
83
+ klass: Pydantic BaseModel class or schema dict to process
84
+ data: Input data to fill with defaults (can be dict, primitive, list, or None)
85
+ use_defaults: If True, use type-based defaults for missing required fields.
86
+ If False, raise ValueError for missing required fields.
87
+ definitions: Schema definitions for definition-ref fields (e.g., VaultSecret).
88
+ Automatically extracted from models with "definitions" schema type.
89
+
90
+ Returns:
91
+ Data with defaults filled in. Return type matches input data structure.
92
+
93
+ Field handling behavior:
94
+ - Optional fields (T | None): Default to None if missing
95
+ - Required primitives: Use DEFAULT_STRING, DEFAULT_INT, or DEFAULT_BOOL
96
+ - Fields with explicit defaults: Use the provided default value
97
+ - Lists: Default to empty list [] if missing, recursively process items
98
+ - Nested models: Recursively process with data_default_none
99
+ - Union types: Try each variant until one validates ("first match wins")
100
+
101
+ Example:
102
+ >>> class User(BaseModel):
103
+ ... name: str
104
+ ... age: int | None
105
+ >>> data_default_none(User, {})
106
+ {'name': 'I was too lazy to define a string here', 'age': None}
107
+ """
108
+ # =========================================================================
109
+ # Extract schema from BaseModel class or use provided schema dict
110
+ # =========================================================================
111
+ if isinstance(klass, dict):
112
+ # Recursive calls pass schema dict directly
113
+ schema = klass
114
+ else:
115
+ # Extract schema from Pydantic BaseModel class
116
+ match klass.__pydantic_core_schema__["type"]:
117
+ case "definitions":
118
+ # Models using definition-ref (e.g., VaultSecret fragments)
119
+ schema = klass.__pydantic_core_schema__["schema"]["schema"]
120
+ definitions = klass.__pydantic_core_schema__["definitions"]
121
+ case _:
122
+ # Standard models
123
+ schema = klass.__pydantic_core_schema__["schema"]
124
+
125
+ # =========================================================================
126
+ # Get fields to process - either model fields or single schema
127
+ # =========================================================================
128
+ if schema["type"] == "model-fields":
129
+ # Multiple fields in a model
130
+ fields = schema["fields"].items()
131
+ else:
132
+ # Single field schema (used in recursive calls for field processing)
133
+ fields = [(None, schema)]
134
+
135
+ # =========================================================================
136
+ # Process each field according to its schema type
137
+ # =========================================================================
138
+ for name, field_info in fields:
139
+ # Use alias if defined (e.g., "serverUrl" instead of "server_url")
140
+ alias = field_info.get("validation_alias", name)
141
+
142
+ match field_info["type"]:
143
+ # =================================================================
144
+ # PRIMITIVE TYPES: str, int, bool
145
+ # =================================================================
146
+ case "str" | "int" | "bool" | "json":
147
+ # If data is provided, return it as-is (don't override user data)
148
+ if data is not None:
149
+ return data
150
+
151
+ # If no defaults allowed, raise error for missing required field
152
+ if not use_defaults:
153
+ raise ValueError(f"Field {alias or ''} is required but not set.")
154
+
155
+ # Return type-specific default value
156
+ match field_info["type"]:
157
+ case "str":
158
+ return DEFAULT_STRING
159
+ case "int":
160
+ return DEFAULT_INT
161
+ case "bool":
162
+ return DEFAULT_BOOL
163
+ case "json":
164
+ return DEFAULT_JSON_STR
165
+
166
+ # =================================================================
167
+ # FIELDS WITH EXPLICIT DEFAULT VALUES
168
+ # =================================================================
169
+ case "default":
170
+ # If no data provided, use the field's default value
171
+ if data is None:
172
+ return field_info["default"]
173
+
174
+ # Data exists - recursively process it in case it's a complex type
175
+ return _process_field_with_alias(
176
+ field_info, data, alias, use_defaults, definitions
177
+ )
178
+
179
+ # =================================================================
180
+ # NULLABLE/OPTIONAL FIELDS: T | None
181
+ # =================================================================
182
+ case "nullable":
183
+ # If no data provided, optional fields default to None
184
+ if data is None:
185
+ return None
186
+
187
+ # Data exists - recursively process the non-None value
188
+ return _process_field_with_alias(
189
+ field_info, data, alias, use_defaults, definitions
190
+ )
191
+
192
+ # =================================================================
193
+ # MODEL FIELDS: Nested Pydantic models within parent model
194
+ # =================================================================
195
+ case "model-field":
196
+ # Ensure data is a dict (create empty dict if needed)
197
+ # This allows processing models even when parent data is missing
198
+ if not isinstance(data, MutableMapping):
199
+ data = {}
200
+
201
+ # Recursively process the nested model
202
+ data[alias] = data_default_none(
203
+ field_info["schema"],
204
+ data.get(alias),
205
+ use_defaults=use_defaults,
206
+ definitions=definitions,
207
+ )
208
+
209
+ # =================================================================
210
+ # STANDALONE MODELS: BaseModel used as field type
211
+ # =================================================================
212
+ case "model":
213
+ # Recursively process nested BaseModel
214
+ return _process_field_with_alias(
215
+ field_info, data, alias, use_defaults, definitions
216
+ )
217
+
218
+ # =================================================================
219
+ # DEFINITION REFERENCES: e.g., VaultSecret fragments
220
+ # =================================================================
221
+ case "definition-ref":
222
+ # Definition-ref fields require a MutableMapping data structure
223
+ assert isinstance(data, MutableMapping)
224
+
225
+ # Definitions are required for resolving references
226
+ if not definitions:
227
+ raise RuntimeError(
228
+ "definitions parameter is required for definition-ref fields"
229
+ )
230
+
231
+ # Find the referenced schema definition by ref ID
232
+ ref_schema = next(
233
+ definition
234
+ for definition in definitions
235
+ if definition["ref"] == field_info["schema_ref"]
236
+ )
237
+
238
+ # Recursively process using the referenced schema
239
+ value = data_default_none(
240
+ ref_schema["schema"],
241
+ data.get(alias) if alias else data,
242
+ use_defaults=use_defaults,
243
+ definitions=definitions,
244
+ )
245
+
246
+ # For unnamed fields, return value directly
247
+ if not alias:
248
+ return value
249
+
250
+ data[alias] = value
251
+
252
+ # =================================================================
253
+ # UNION TYPES: T1 | T2 | ...
254
+ # =================================================================
255
+ case "union":
256
+ # Strategy: Try each union variant until one validates successfully
257
+ # This is a "first match wins" approach
258
+ for sub_field in field_info["choices"]:
259
+ try:
260
+ # Try to process data with this union variant
261
+ # use_defaults=False ensures we only match existing data
262
+ sub_data = data_default_none(
263
+ sub_field,
264
+ # don't alter data structure here
265
+ dict(data) if isinstance(data, MutableMapping) else data,
266
+ use_defaults=use_defaults,
267
+ definitions=definitions,
268
+ )
269
+
270
+ # Validate the result matches the expected type
271
+ match sub_field["type"]:
272
+ case "str":
273
+ if not isinstance(sub_data, str):
274
+ raise ValueError()
275
+ case "int":
276
+ if not isinstance(sub_data, int):
277
+ raise ValueError()
278
+ case "bool":
279
+ if not isinstance(sub_data, bool):
280
+ raise ValueError()
281
+ case "model":
282
+ if not isinstance(sub_data, dict):
283
+ raise ValueError()
284
+ # Validate the dict can be instantiated as the model
285
+ sub_field["cls"].model_validate(sub_data)
286
+ if isinstance(data, MutableMapping):
287
+ data.update(sub_data)
288
+
289
+ # If we get here, this union variant matched successfully
290
+ return data
291
+ except (ValidationError, ValueError):
292
+ # This variant didn't match, try the next one
293
+ continue
294
+
295
+ # =================================================================
296
+ # LIST TYPES: list[T]
297
+ # =================================================================
298
+ case "list":
299
+ # If data is not a list, default to empty list
300
+ if not isinstance(data, list):
301
+ return []
302
+
303
+ # Recursively process each list item
304
+ return [
305
+ data_default_none(
306
+ field_info["items_schema"],
307
+ item.get(alias)
308
+ if isinstance(item, MutableMapping) and alias
309
+ else item,
310
+ use_defaults=use_defaults,
311
+ definitions=definitions,
312
+ )
313
+ for item in data
49
314
  ]
50
- elif field.sub_fields:
51
- if all(
52
- isinstance(sub_field.type_, type)
53
- and issubclass(sub_field.type_, BaseModel)
54
- for sub_field in field.sub_fields
55
- ):
56
- # Union[ClassA, ClassB] field
57
- for sub_field in field.sub_fields:
58
- if isinstance(data[field.alias], dict):
59
- try:
60
- d = dict(data[field.alias])
61
- d.update(data_default_none(sub_field.type_, d))
62
- # Lets confirm we found a matching union class
63
- sub_field.type_(**d)
64
- data[field.alias] = d
65
- break
66
- except ValidationError:
67
- continue
68
- elif isinstance(data[field.alias], list) and len(field.sub_fields) == 1:
69
- # list[Union[ClassA, ClassB]] field
70
- for sub_data in data[field.alias]:
71
- for sub_field in field.sub_fields[0].sub_fields or []:
72
- try:
73
- d = dict(sub_data)
74
- d.update(data_default_none(sub_field.type_, d))
75
- # Lets confirm we found a matching union class
76
- sub_field.type_(**d)
77
- sub_data.update(d)
78
- break
79
- except ValidationError:
80
- continue
81
315
 
82
316
  return data
83
317
 
@@ -89,28 +323,16 @@ class CSV(UserList[str]):
89
323
  """
90
324
 
91
325
  @classmethod
92
- def __get_validators__(cls) -> Generator[Callable, None, None]: # noqa: PLW3201
93
- yield cls.validate
94
- yield cls.length_validator
95
-
96
- @classmethod
97
- def validate(cls, value: str) -> list[str]:
98
- items = [] if not value else value.split(",")
99
- return items
326
+ def __get_pydantic_core_schema__( # noqa: PLW3201
327
+ cls, source: type[Any], handler: GetCoreSchemaHandler
328
+ ) -> core_schema.CoreSchema:
329
+ return core_schema.with_info_before_validator_function(
330
+ cls._validate, core_schema.list_schema()
331
+ )
100
332
 
101
333
  @classmethod
102
- def length_validator(
103
- cls, v: "list[str]", values: dict, field: ModelField
104
- ) -> "list[str]":
105
- min_items = field.field_info.extra.get("csv_min_items")
106
- max_items = field.field_info.extra.get("csv_max_items")
107
-
108
- v_len = len(v)
109
- if min_items is not None and v_len < min_items:
110
- raise pydantic_errors.ListMinLengthError(limit_value=min_items)
111
- if max_items is not None and v_len > max_items:
112
- raise pydantic_errors.ListMaxLengthError(limit_value=max_items)
113
- return v
334
+ def _validate(cls, __input_value: str, _: Any) -> list[str]:
335
+ return [] if not __input_value else __input_value.split(",")
114
336
 
115
337
 
116
338
  def cron_validator(value: str) -> str:
@@ -1,9 +1,9 @@
1
1
  from collections.abc import Iterable
2
- from datetime import datetime
3
2
  from pathlib import Path
4
3
 
5
4
  from ruamel.yaml.scalarstring import PreservedScalarString
6
5
 
6
+ from reconcile.utils.datetime_util import utc_now
7
7
  from reconcile.utils.gitlab_api import GitLabApi
8
8
  from reconcile.utils.mr.base import (
9
9
  MergeRequestBase,
@@ -26,7 +26,7 @@ class CreateAppInterfaceReporter(MergeRequestBase):
26
26
 
27
27
  self.labels = [AUTO_MERGE]
28
28
 
29
- now = datetime.now()
29
+ now = utc_now()
30
30
  self.isodate = now.isoformat()
31
31
  self.ts = now.strftime("%Y%m%d%H%M%S")
32
32
 
@@ -1,9 +1,9 @@
1
1
  import logging
2
- from datetime import datetime
3
2
  from pathlib import Path
4
3
 
5
4
  from pydantic import BaseModel
6
5
 
6
+ from reconcile.utils.datetime_util import utc_now
7
7
  from reconcile.utils.gitlab_api import GitLabApi
8
8
  from reconcile.utils.mr.base import (
9
9
  MergeRequestBase,
@@ -35,7 +35,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
35
35
  email_base_path: Path = Path("data") / "app-interface" / "emails",
36
36
  dry_run: bool = False,
37
37
  ):
38
- self._notification_as_dict = notification.dict()
38
+ self._notification_as_dict = notification.model_dump()
39
39
  super().__init__()
40
40
  self._notification = notification
41
41
  self._email_base_path = email_base_path
@@ -58,7 +58,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
58
58
  )
59
59
 
60
60
  def process(self, gitlab_cli: GitLabApi) -> None:
61
- now = datetime.now()
61
+ now = utc_now()
62
62
  ts = now.strftime("%Y%m%d%H%M%S")
63
63
  short_date = now.strftime("%Y-%m-%d")
64
64
 
@@ -1,14 +1,13 @@
1
1
  import logging
2
2
  from abc import abstractmethod
3
3
  from collections.abc import Sequence
4
- from datetime import UTC, date
5
- from datetime import datetime as dt
6
4
  from pathlib import Path
7
5
  from typing import TypeVar
8
6
 
9
7
  from jinja2 import Template
10
8
  from pydantic import BaseModel
11
9
 
10
+ from reconcile.utils.datetime_util import utc_now
12
11
  from reconcile.utils.gitlab_api import GitLabApi
13
12
  from reconcile.utils.mr.base import MergeRequestBase
14
13
  from reconcile.utils.mr.labels import AUTO_MERGE
@@ -27,7 +26,7 @@ class UpdateAccessReportBase(MergeRequestBase):
27
26
  self.labels = [AUTO_MERGE]
28
27
  self._users = users
29
28
  self._workbook_file_name = str(workbook_path)
30
- self._isodate = dt.now(tz=UTC).isoformat()
29
+ self._isodate = utc_now().isoformat()
31
30
  self._dry_run = dry_run
32
31
 
33
32
  @property
@@ -65,7 +64,7 @@ class UpdateAccessReportBase(MergeRequestBase):
65
64
 
66
65
  def _render_tracking_table_row(self, old_number_of_users: int) -> str:
67
66
  # | Date Reviewed | Number of Current Users | +/- Red Hat Users |
68
- return f"| {date.today()} | {len(self._users)} | {len(self._users) - old_number_of_users} |\n"
67
+ return f"| {utc_now().date()} | {len(self._users)} | {len(self._users) - old_number_of_users} |\n"
69
68
 
70
69
  def _update_workbook(self, workbook_md: str) -> str:
71
70
  new_workbook_md = ""
@@ -2,7 +2,7 @@ from collections.abc import Iterable
2
2
  from enum import Enum
3
3
  from pathlib import Path
4
4
 
5
- from pydantic import BaseModel, validator
5
+ from pydantic import BaseModel, field_validator
6
6
  from ruamel import yaml
7
7
 
8
8
  from reconcile.utils.gitlab_api import GitLabApi
@@ -23,7 +23,8 @@ class PathSpec(BaseModel):
23
23
  type: PathTypes
24
24
  path: str
25
25
 
26
- @validator("path")
26
+ @field_validator("path")
27
+ @classmethod
27
28
  def prepend_data_to_path(cls, v: str) -> str:
28
29
  return "data" + v
29
30