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
@@ -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,
@@ -32,7 +32,12 @@ from reconcile.utils.openshift_resource import (
32
32
  ResourceInventory,
33
33
  base64_encode_secret_field_value,
34
34
  )
35
- from reconcile.utils.rhcsv2_certs import RhcsV2Cert, generate_cert
35
+ from reconcile.utils.rhcsv2_certs import (
36
+ CertificateFormat,
37
+ RhcsV2CertPem,
38
+ RhcsV2CertPkcs12,
39
+ generate_cert,
40
+ )
36
41
  from reconcile.utils.runtime.integration import DesiredStateShardConfig
37
42
  from reconcile.utils.secret_reader import create_secret_reader
38
43
  from reconcile.utils.semver_helper import make_semver
@@ -40,7 +45,6 @@ from reconcile.utils.vault import SecretNotFoundError, VaultClient
40
45
 
41
46
  QONTRACT_INTEGRATION = "openshift-rhcs-certs"
42
47
  QONTRACT_INTEGRATION_VERSION = make_semver(1, 9, 3)
43
- PROVIDERS = ["rhcs-cert"]
44
48
 
45
49
 
46
50
  def desired_state_shard_config() -> DesiredStateShardConfig:
@@ -67,8 +71,29 @@ class OpenshiftRhcsCertExpiration(GaugeMetric):
67
71
  return "qontract_reconcile_rhcs_cert_expiration_timestamp"
68
72
 
69
73
 
70
- def _is_rhcs_cert(obj: Any) -> bool:
71
- return getattr(obj, "provider", None) == "rhcs-cert"
74
+ def _generate_placeholder_cert(
75
+ cert_format: CertificateFormat,
76
+ ) -> RhcsV2CertPem | RhcsV2CertPkcs12:
77
+ match cert_format:
78
+ case CertificateFormat.PKCS12:
79
+ return RhcsV2CertPkcs12(
80
+ pkcs12_keystore="PLACEHOLDER_KEYSTORE",
81
+ pkcs12_truststore="PLACEHOLDER_TRUSTSTORE",
82
+ expiration_timestamp=int(time.time()),
83
+ )
84
+ case CertificateFormat.PEM:
85
+ return RhcsV2CertPem(
86
+ certificate="PLACEHOLDER_CERT",
87
+ private_key="PLACEHOLDER_PRIVATE_KEY",
88
+ ca_cert="PLACEHOLDER_CA_CERT",
89
+ expiration_timestamp=int(time.time()),
90
+ )
91
+
92
+
93
+ def get_certificate_format(
94
+ cert_resource: OpenshiftResourceRhcsCert,
95
+ ) -> CertificateFormat:
96
+ return CertificateFormat(cert_resource.certificate_format or "PEM")
72
97
 
73
98
 
74
99
  def get_namespaces_with_rhcs_certs(
@@ -77,26 +102,33 @@ def get_namespaces_with_rhcs_certs(
77
102
  ) -> list[NamespaceV1]:
78
103
  result: list[NamespaceV1] = []
79
104
  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]
105
+ ob.aggregate_shared_resources_typed(ns)
81
106
  if (
82
107
  integration_is_enabled(QONTRACT_INTEGRATION, ns.cluster)
83
108
  and not bool(ns.delete)
84
109
  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 [])
110
+ and ns.openshift_resources
86
111
  ):
87
112
  result.append(ns)
88
113
  return result
89
114
 
90
115
 
91
116
  def construct_rhcs_cert_oc_secret(
92
- secret_name: str, cert: Mapping[str, Any], annotations: Mapping[str, str]
117
+ secret_name: str,
118
+ cert: Mapping[str, Any],
119
+ annotations: Mapping[str, str],
120
+ certificate_format: CertificateFormat,
93
121
  ) -> OR:
94
122
  body: dict[str, Any] = {
95
123
  "apiVersion": "v1",
96
124
  "kind": "Secret",
97
- "type": "kubernetes.io/tls",
98
125
  "metadata": {"name": secret_name, "annotations": annotations},
99
126
  }
127
+ match certificate_format:
128
+ case CertificateFormat.PKCS12:
129
+ body["type"] = "Opaque"
130
+ case CertificateFormat.PEM:
131
+ body["type"] = "kubernetes.io/tls"
100
132
  for k, v in cert.items():
101
133
  v = base64_encode_secret_field_value(v)
102
134
  body.setdefault("data", {})[k] = v
@@ -105,7 +137,7 @@ def construct_rhcs_cert_oc_secret(
105
137
 
106
138
  def cert_expires_within_threshold(
107
139
  ns: NamespaceV1,
108
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
140
+ cert_resource: OpenshiftResourceRhcsCert,
109
141
  vault_cert_secret: Mapping[str, Any],
110
142
  ) -> bool:
111
143
  auto_renew_threshold_days = cert_resource.auto_renew_threshold_days or 7
@@ -121,7 +153,7 @@ def cert_expires_within_threshold(
121
153
 
122
154
  def get_vault_cert_secret(
123
155
  ns: NamespaceV1,
124
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
156
+ cert_resource: OpenshiftResourceRhcsCert,
125
157
  vault: VaultClient,
126
158
  vault_base_path: str,
127
159
  ) -> dict | None:
@@ -140,7 +172,7 @@ def get_vault_cert_secret(
140
172
  def generate_vault_cert_secret(
141
173
  dry_run: bool,
142
174
  ns: NamespaceV1,
143
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
175
+ cert_resource: OpenshiftResourceRhcsCert,
144
176
  vault: VaultClient,
145
177
  vault_base_path: str,
146
178
  issuer_url: str,
@@ -149,18 +181,19 @@ def generate_vault_cert_secret(
149
181
  logging.info(
150
182
  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
183
  )
152
- sa_password = vault.read(cert_resource.service_account_password.dict())
184
+ sa_password = vault.read(cert_resource.service_account_password.model_dump())
185
+ cert_format = get_certificate_format(cert_resource)
186
+
153
187
  if dry_run:
154
- rhcs_cert = RhcsV2Cert(
155
- certificate="PLACEHOLDER_CERT",
156
- private_key="PLACEHOLDER_PRIVATE_KEY",
157
- ca_cert="PLACEHOLDER_CA_CERT",
158
- expiration_timestamp=int(time.time()),
159
- )
188
+ rhcs_cert = _generate_placeholder_cert(cert_format)
160
189
  else:
161
190
  try:
162
191
  rhcs_cert = generate_cert(
163
- issuer_url, cert_resource.service_account_name, sa_password, ca_cert_url
192
+ issuer_url=issuer_url,
193
+ uid=cert_resource.service_account_name,
194
+ pwd=sa_password,
195
+ ca_url=ca_cert_url,
196
+ cert_format=cert_format,
164
197
  )
165
198
  except ValueError as e:
166
199
  raise Exception(
@@ -171,18 +204,18 @@ def generate_vault_cert_secret(
171
204
  )
172
205
  vault.write(
173
206
  secret={
174
- "data": rhcs_cert.dict(by_alias=True),
207
+ "data": rhcs_cert.model_dump(by_alias=True, exclude_none=True),
175
208
  "path": f"{vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}",
176
209
  },
177
210
  decode_base64=False,
178
211
  )
179
- return rhcs_cert.dict(by_alias=True)
212
+ return rhcs_cert.model_dump(by_alias=True, exclude_none=True)
180
213
 
181
214
 
182
215
  def fetch_openshift_resource_for_cert_resource(
183
216
  dry_run: bool,
184
217
  ns: NamespaceV1,
185
- cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
218
+ cert_resource: OpenshiftResourceRhcsCert,
186
219
  vault: VaultClient,
187
220
  rhcs_settings: RhcsProviderSettingsV1,
188
221
  ) -> OR:
@@ -218,6 +251,7 @@ def fetch_openshift_resource_for_cert_resource(
218
251
  secret_name=cert_resource.secret_name,
219
252
  cert=vault_cert_secret,
220
253
  annotations=cert_resource.annotations or {},
254
+ certificate_format=get_certificate_format(cert_resource),
221
255
  )
222
256
 
223
257
 
@@ -231,18 +265,13 @@ def fetch_desired_state(
231
265
  cert_provider = get_rhcs_provider_settings(query_func=query_func)
232
266
  for ns in namespaces:
233
267
  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
- )
268
+ ri.add_desired_resource(
269
+ cluster=ns.cluster.name,
270
+ namespace=ns.name,
271
+ resource=fetch_openshift_resource_for_cert_resource(
272
+ dry_run, ns, cert_resource, vault, cert_provider
273
+ ),
274
+ )
246
275
 
247
276
 
248
277
  @defer
@@ -277,7 +306,7 @@ def run(
277
306
  state_specs = ob.init_specs_to_fetch(
278
307
  ri,
279
308
  oc_map,
280
- namespaces=[ns.dict(by_alias=True) for ns in namespaces],
309
+ namespaces=[ns.model_dump(by_alias=True) for ns in namespaces],
281
310
  override_managed_types=["Secret"],
282
311
  )
283
312
  for spec in state_specs:
@@ -295,3 +324,11 @@ def run(
295
324
  ob.publish_metrics(ri, QONTRACT_INTEGRATION)
296
325
  if ri.has_error_registered():
297
326
  sys.exit(1)
327
+
328
+
329
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
330
+ if not (query_func := kwargs.get("query_func")):
331
+ query_func = gql.get_api().query
332
+
333
+ cluster_name = kwargs.get("cluster_name")
334
+ 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
@@ -1,14 +1,11 @@
1
1
  import logging
2
2
  from collections.abc import Callable
3
3
  from datetime import (
4
- UTC,
5
4
  datetime,
6
5
  timedelta,
7
6
  )
8
7
  from typing import Any
9
8
 
10
- from dateutil import parser
11
-
12
9
  from reconcile.gql_definitions.fragments.pipeline_provider_retention import (
13
10
  PipelineProviderRetention,
14
11
  )
@@ -19,6 +16,7 @@ from reconcile.typed_queries.tekton_pipeline_providers import (
19
16
  get_tekton_pipeline_providers,
20
17
  )
21
18
  from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
19
+ from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
22
20
  from reconcile.utils.defer import defer
23
21
  from reconcile.utils.oc_map import (
24
22
  OCLogMsg,
@@ -35,7 +33,7 @@ def within_retention_days(
35
33
  resource: dict[str, Any], days: int, now_date: datetime
36
34
  ) -> bool:
37
35
  metadata = resource["metadata"]
38
- creation_date = parser.parse(metadata["creationTimestamp"])
36
+ creation_date = from_utc_iso_format(metadata["creationTimestamp"])
39
37
  interval = now_date.timestamp() - creation_date.timestamp()
40
38
 
41
39
  return interval < timedelta(days=days).total_seconds()
@@ -69,7 +67,7 @@ def run(
69
67
  use_jump_host: bool = True,
70
68
  defer: Callable | None = None,
71
69
  ) -> None:
72
- now_date = datetime.now(UTC)
70
+ now_date = utc_now()
73
71
  vault_settings = get_app_interface_vault_settings()
74
72
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
75
73
  pipeline_providers = get_tekton_pipeline_providers()
@@ -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,
@@ -3,7 +3,6 @@ from collections.abc import (
3
3
  Callable,
4
4
  Iterable,
5
5
  )
6
- from datetime import datetime
7
6
 
8
7
  from reconcile import queries
9
8
  from reconcile.gql_definitions.common.clusters import ClusterV1
@@ -13,6 +12,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
13
12
  )
14
13
  from reconcile.typed_queries.clusters import get_clusters
15
14
  from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
15
+ from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
16
16
  from reconcile.utils.defer import defer
17
17
  from reconcile.utils.oc_map import (
18
18
  OCLogMsg,
@@ -101,7 +101,7 @@ def notify_upgrades_start(
101
101
  state: State,
102
102
  slack: SlackApi | None,
103
103
  ) -> None:
104
- now = datetime.utcnow()
104
+ now = utc_now()
105
105
  for cluster in clusters:
106
106
  if cluster.spec and not cluster.spec.hypershift:
107
107
  upgrade_at, version = _get_start_osd(oc_map, cluster.name)
@@ -113,7 +113,7 @@ def notify_upgrades_start(
113
113
  continue
114
114
 
115
115
  if upgrade_at and version:
116
- upgrade_at_obj = datetime.strptime(upgrade_at, "%Y-%m-%dT%H:%M:%SZ")
116
+ upgrade_at_obj = from_utc_iso_format(upgrade_at)
117
117
  state_key = f"{cluster.name}-{upgrade_at}1"
118
118
  # if this is the first iteration in which 'now' had passed
119
119
  # the upgrade at date time, we send a notification
@@ -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)
reconcile/quay_base.py CHANGED
@@ -1,4 +1,4 @@
1
- from collections import namedtuple
1
+ from collections import UserDict, namedtuple
2
2
  from typing import Any, TypedDict
3
3
 
4
4
  from reconcile import queries
@@ -10,22 +10,34 @@ OrgKey = namedtuple("OrgKey", ["instance", "org_name"])
10
10
 
11
11
  class OrgInfo(TypedDict):
12
12
  url: str
13
- api: QuayApi
14
13
  push_token: dict[str, str] | None
15
14
  teams: list[str]
16
15
  managedRepos: bool
17
16
  mirror: OrgKey | None
18
17
  mirror_filters: dict[str, Any]
18
+ api: QuayApi
19
+
19
20
 
21
+ class QuayApiStore(UserDict[OrgKey, OrgInfo]):
22
+ def __init__(self) -> None:
23
+ super().__init__(get_quay_api_store())
20
24
 
21
- QuayApiStore = dict[OrgKey, OrgInfo]
25
+ def cleanup(self) -> None:
26
+ """Close all QuayApi sessions."""
27
+ for org_info in self.data.values():
28
+ org_info["api"].cleanup()
22
29
 
30
+ def __enter__(self) -> "QuayApiStore":
31
+ return self
23
32
 
24
- def get_quay_api_store() -> QuayApiStore:
33
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
34
+ self.cleanup()
35
+
36
+
37
+ def get_quay_api_store() -> dict[OrgKey, OrgInfo]:
25
38
  """
26
39
  Returns a dictionary with a key for each Quay organization
27
40
  managed in app-interface.
28
- Each key contains an initiated QuayApi instance.
29
41
  """
30
42
  quay_orgs = queries.get_quay_orgs()
31
43
  settings = queries.get_app_interface_settings()
@@ -61,14 +73,21 @@ def get_quay_api_store() -> QuayApiStore:
61
73
  else:
62
74
  push_token = None
63
75
 
76
+ # Create QuayApi instance for this org
77
+ api = QuayApi(
78
+ token=token,
79
+ organization=org_name,
80
+ base_url=base_url,
81
+ )
82
+
64
83
  org_info: OrgInfo = {
65
84
  "url": base_url,
66
- "api": QuayApi(token, org_name, base_url=base_url),
67
85
  "push_token": push_token,
68
86
  "teams": org_data.get("managedTeams") or [],
69
87
  "managedRepos": bool(org_data.get("managedRepos")),
70
88
  "mirror": mirror,
71
89
  "mirror_filters": mirror_filters,
90
+ "api": api,
72
91
  }
73
92
 
74
93
  store[org_key] = org_info