qontract-reconcile 0.10.2.dev310__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 (400) hide show
  1. {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +396 -391
  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 +5 -5
  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 +125 -84
  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 +12 -4
  28. reconcile/cli.py +111 -18
  29. reconcile/cluster_deployment_mapper.py +2 -3
  30. reconcile/dashdotdb_dora.py +5 -12
  31. reconcile/dashdotdb_slo.py +1 -1
  32. reconcile/database_access_manager.py +125 -121
  33. reconcile/deadmanssnitch.py +1 -5
  34. reconcile/dynatrace_token_provider/integration.py +1 -1
  35. reconcile/endpoints_discovery/integration.py +4 -1
  36. reconcile/endpoints_discovery/merge_request.py +1 -1
  37. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  38. reconcile/external_resources/factories.py +5 -12
  39. reconcile/external_resources/integration.py +1 -1
  40. reconcile/external_resources/manager.py +8 -5
  41. reconcile/external_resources/meta.py +0 -1
  42. reconcile/external_resources/metrics.py +1 -1
  43. reconcile/external_resources/model.py +20 -20
  44. reconcile/external_resources/reconciler.py +7 -4
  45. reconcile/external_resources/secrets_sync.py +10 -14
  46. reconcile/external_resources/state.py +26 -16
  47. reconcile/fleet_labeler/integration.py +1 -1
  48. reconcile/gabi_authorized_users.py +8 -5
  49. reconcile/gcp_image_mirror.py +2 -2
  50. reconcile/github_org.py +1 -1
  51. reconcile/github_owners.py +4 -0
  52. reconcile/gitlab_housekeeping.py +13 -15
  53. reconcile/gitlab_members.py +6 -12
  54. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  55. reconcile/gitlab_owners.py +15 -11
  56. reconcile/gitlab_permissions.py +8 -12
  57. reconcile/glitchtip_project_alerts/integration.py +3 -1
  58. reconcile/gql_definitions/acs/acs_instances.py +10 -10
  59. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  60. reconcile/gql_definitions/acs/acs_rbac.py +6 -6
  61. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
  62. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
  63. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
  64. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  65. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  66. reconcile/gql_definitions/automated_actions/instance.py +51 -12
  67. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
  68. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
  69. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
  70. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
  71. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
  72. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  73. reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
  74. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  75. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  76. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
  77. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
  78. reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
  79. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  80. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  81. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_roles.py +120 -0
  84. reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
  85. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  87. reconcile/gql_definitions/common/apps.py +5 -5
  88. reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
  89. reconcile/gql_definitions/common/aws_vpcs.py +11 -11
  90. reconcile/gql_definitions/common/clusters.py +37 -35
  91. reconcile/gql_definitions/common/clusters_minimal.py +14 -14
  92. reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
  93. reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
  94. reconcile/gql_definitions/common/github_orgs.py +10 -10
  95. reconcile/gql_definitions/common/jira_settings.py +10 -10
  96. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  97. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  98. reconcile/gql_definitions/common/namespaces.py +42 -44
  99. reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
  100. reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
  101. reconcile/gql_definitions/common/ocm_environments.py +19 -19
  102. reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
  103. reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
  104. reconcile/gql_definitions/common/pipeline_providers.py +29 -29
  105. reconcile/gql_definitions/common/quay_instances.py +5 -5
  106. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  107. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  108. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  109. reconcile/gql_definitions/common/saas_files.py +44 -44
  110. reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
  111. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  112. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  113. reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
  114. reconcile/gql_definitions/common/state_aws_account.py +7 -8
  115. reconcile/gql_definitions/common/users.py +5 -5
  116. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  117. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  118. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  119. reconcile/gql_definitions/cost_report/settings.py +9 -9
  120. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
  121. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
  122. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  123. reconcile/gql_definitions/email_sender/apps.py +5 -5
  124. reconcile/gql_definitions/email_sender/emails.py +8 -8
  125. reconcile/gql_definitions/email_sender/users.py +6 -6
  126. reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
  127. reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
  128. reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
  129. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
  130. reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
  131. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  132. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
  133. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  135. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  136. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  138. reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
  139. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  141. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  142. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  143. reconcile/gql_definitions/fragments/disable.py +5 -5
  144. reconcile/gql_definitions/fragments/email_service.py +5 -5
  145. reconcile/gql_definitions/fragments/email_user.py +5 -5
  146. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  147. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  148. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  149. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  150. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  151. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  152. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  153. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  154. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  155. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  156. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  157. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  158. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  159. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  160. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  161. reconcile/gql_definitions/fragments/user.py +5 -5
  162. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  163. reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
  164. reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
  165. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
  166. reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
  167. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
  168. reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
  169. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
  170. reconcile/gql_definitions/integrations/integrations.py +48 -51
  171. reconcile/gql_definitions/introspection.json +3510 -1865
  172. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
  173. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
  174. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  175. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
  176. reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
  177. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  178. reconcile/gql_definitions/ldap_groups/settings.py +9 -9
  179. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  180. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  181. reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
  182. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  183. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
  184. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  185. reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
  186. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
  187. reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
  188. reconcile/gql_definitions/rhcs/certs.py +33 -87
  189. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  190. reconcile/gql_definitions/rhidp/organizations.py +18 -18
  191. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  192. reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
  193. reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
  194. reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
  195. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  196. reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
  197. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  198. reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
  199. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  200. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  201. reconcile/gql_definitions/status_board/status_board.py +6 -7
  202. reconcile/gql_definitions/statuspage/statuspages.py +9 -9
  203. reconcile/gql_definitions/templating/template_collection.py +5 -5
  204. reconcile/gql_definitions/templating/templates.py +5 -5
  205. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
  206. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
  207. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
  208. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
  209. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
  210. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
  211. reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
  212. reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
  213. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  214. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +450 -402
  215. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
  216. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
  217. reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
  218. reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
  219. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
  220. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  221. reconcile/integrations_manager.py +3 -3
  222. reconcile/jenkins_job_builder.py +1 -1
  223. reconcile/jenkins_worker_fleets.py +80 -11
  224. reconcile/jira_permissions_validator.py +237 -122
  225. reconcile/ldap_groups/integration.py +1 -1
  226. reconcile/ocm/types.py +35 -56
  227. reconcile/ocm_aws_infrastructure_access.py +1 -1
  228. reconcile/ocm_clusters.py +4 -4
  229. reconcile/ocm_labels/integration.py +3 -2
  230. reconcile/ocm_machine_pools.py +33 -27
  231. reconcile/openshift_base.py +122 -10
  232. reconcile/openshift_cluster_bots.py +5 -5
  233. reconcile/openshift_groups.py +5 -0
  234. reconcile/openshift_limitranges.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +97 -101
  237. reconcile/openshift_resources_base.py +10 -5
  238. reconcile/openshift_rhcs_certs.py +77 -40
  239. reconcile/openshift_rolebindings.py +230 -130
  240. reconcile/openshift_saas_deploy.py +6 -7
  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 +8 -7
  244. reconcile/openshift_tekton_resources.py +1 -1
  245. reconcile/openshift_upgrade_watcher.py +4 -4
  246. reconcile/openshift_users.py +5 -3
  247. reconcile/oum/labelset.py +5 -3
  248. reconcile/oum/models.py +1 -4
  249. reconcile/oum/providers.py +1 -1
  250. reconcile/prometheus_rules_tester/integration.py +4 -4
  251. reconcile/quay_mirror.py +1 -1
  252. reconcile/queries.py +131 -0
  253. reconcile/requests_sender.py +8 -3
  254. reconcile/resource_scraper.py +1 -5
  255. reconcile/rhidp/common.py +3 -5
  256. reconcile/rhidp/sso_client/base.py +19 -10
  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/sendgrid_teammates.py +20 -9
  260. reconcile/skupper_network/integration.py +2 -2
  261. reconcile/slack_usergroups.py +35 -14
  262. reconcile/sql_query.py +1 -0
  263. reconcile/status.py +2 -2
  264. reconcile/status_board.py +6 -6
  265. reconcile/statuspage/atlassian.py +7 -7
  266. reconcile/statuspage/integrations/maintenances.py +4 -3
  267. reconcile/statuspage/page.py +4 -9
  268. reconcile/statuspage/status.py +5 -8
  269. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +5 -1
  270. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
  271. reconcile/templating/lib/merge_request_manager.py +2 -2
  272. reconcile/templating/lib/rendering.py +3 -3
  273. reconcile/templating/renderer.py +12 -13
  274. reconcile/terraform_aws_route53.py +18 -8
  275. reconcile/terraform_cloudflare_dns.py +3 -3
  276. reconcile/terraform_cloudflare_resources.py +12 -13
  277. reconcile/terraform_cloudflare_users.py +3 -2
  278. reconcile/terraform_init/integration.py +187 -23
  279. reconcile/terraform_repo.py +16 -12
  280. reconcile/terraform_resources.py +18 -10
  281. reconcile/terraform_tgw_attachments.py +28 -20
  282. reconcile/terraform_users.py +27 -22
  283. reconcile/terraform_vpc_peerings.py +15 -3
  284. reconcile/terraform_vpc_resources/integration.py +23 -8
  285. reconcile/typed_queries/app_interface_roles.py +10 -0
  286. reconcile/typed_queries/aws_account_tags.py +41 -0
  287. reconcile/typed_queries/cost_report/app_names.py +1 -1
  288. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  289. reconcile/typed_queries/saas_files.py +13 -13
  290. reconcile/typed_queries/status_board.py +2 -2
  291. reconcile/unleash_feature_toggles/integration.py +4 -2
  292. reconcile/utils/acs/base.py +6 -3
  293. reconcile/utils/acs/policies.py +2 -2
  294. reconcile/utils/aggregated_list.py +4 -3
  295. reconcile/utils/aws_api.py +51 -20
  296. reconcile/utils/aws_api_typed/api.py +38 -9
  297. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  298. reconcile/utils/aws_api_typed/logs.py +73 -0
  299. reconcile/utils/aws_api_typed/organization.py +4 -2
  300. reconcile/utils/binary.py +7 -12
  301. reconcile/utils/datetime_util.py +67 -0
  302. reconcile/utils/deadmanssnitch_api.py +1 -1
  303. reconcile/utils/differ.py +2 -3
  304. reconcile/utils/early_exit_cache.py +11 -12
  305. reconcile/utils/expiration.py +7 -3
  306. reconcile/utils/external_resource_spec.py +24 -1
  307. reconcile/utils/filtering.py +1 -1
  308. reconcile/utils/gitlab_api.py +7 -5
  309. reconcile/utils/glitchtip/client.py +6 -2
  310. reconcile/utils/glitchtip/models.py +25 -28
  311. reconcile/utils/gpg.py +5 -3
  312. reconcile/utils/gql.py +4 -7
  313. reconcile/utils/helm.py +2 -1
  314. reconcile/utils/helpers.py +1 -1
  315. reconcile/utils/imap_client.py +1 -1
  316. reconcile/utils/instrumented_wrappers.py +1 -1
  317. reconcile/utils/internal_groups/client.py +2 -2
  318. reconcile/utils/internal_groups/models.py +8 -17
  319. reconcile/utils/jenkins_api.py +24 -1
  320. reconcile/utils/jinja2/utils.py +6 -8
  321. reconcile/utils/jira_client.py +82 -63
  322. reconcile/utils/jjb_client.py +78 -46
  323. reconcile/utils/jobcontroller/controller.py +2 -2
  324. reconcile/utils/jobcontroller/models.py +17 -1
  325. reconcile/utils/json.py +74 -0
  326. reconcile/utils/ldap_client.py +4 -3
  327. reconcile/utils/lean_terraform_client.py +3 -1
  328. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  329. reconcile/utils/membershipsources/models.py +16 -23
  330. reconcile/utils/membershipsources/resolver.py +4 -2
  331. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  332. reconcile/utils/merge_request_manager/parser.py +6 -6
  333. reconcile/utils/metrics.py +5 -5
  334. reconcile/utils/models.py +304 -82
  335. reconcile/utils/mr/__init__.py +3 -1
  336. reconcile/utils/mr/app_interface_reporter.py +6 -3
  337. reconcile/utils/mr/aws_access.py +1 -1
  338. reconcile/utils/mr/base.py +7 -13
  339. reconcile/utils/mr/clusters_updates.py +4 -2
  340. reconcile/utils/mr/notificator.py +3 -3
  341. reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
  342. reconcile/utils/mr/promote_qontract.py +28 -12
  343. reconcile/utils/mr/update_access_report_base.py +3 -4
  344. reconcile/utils/mr/user_maintenance.py +7 -6
  345. reconcile/utils/oc.py +445 -336
  346. reconcile/utils/oc_filters.py +3 -3
  347. reconcile/utils/ocm/addons.py +0 -1
  348. reconcile/utils/ocm/base.py +18 -21
  349. reconcile/utils/ocm/cluster_groups.py +1 -1
  350. reconcile/utils/ocm/identity_providers.py +2 -2
  351. reconcile/utils/ocm/labels.py +1 -1
  352. reconcile/utils/ocm/ocm.py +81 -71
  353. reconcile/utils/ocm/products.py +9 -3
  354. reconcile/utils/ocm/search_filters.py +3 -6
  355. reconcile/utils/ocm/service_log.py +4 -6
  356. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  357. reconcile/utils/ocm_base_client.py +4 -4
  358. reconcile/utils/openshift_resource.py +83 -52
  359. reconcile/utils/openssl.py +2 -2
  360. reconcile/utils/output.py +3 -2
  361. reconcile/utils/pagerduty_api.py +10 -7
  362. reconcile/utils/promotion_state.py +6 -11
  363. reconcile/utils/raw_github_api.py +11 -8
  364. reconcile/utils/repo_owners.py +21 -29
  365. reconcile/utils/rhcsv2_certs.py +138 -35
  366. reconcile/utils/rosa/session.py +16 -0
  367. reconcile/utils/runtime/integration.py +2 -3
  368. reconcile/utils/runtime/meta.py +2 -1
  369. reconcile/utils/runtime/runner.py +2 -2
  370. reconcile/utils/saasherder/interfaces.py +13 -20
  371. reconcile/utils/saasherder/models.py +25 -21
  372. reconcile/utils/saasherder/saasherder.py +60 -32
  373. reconcile/utils/secret_reader.py +6 -6
  374. reconcile/utils/sharding.py +1 -1
  375. reconcile/utils/slack_api.py +26 -4
  376. reconcile/utils/sloth.py +224 -0
  377. reconcile/utils/sqs_gateway.py +16 -11
  378. reconcile/utils/state.py +2 -1
  379. reconcile/utils/structs.py +1 -1
  380. reconcile/utils/terraform_client.py +29 -26
  381. reconcile/utils/terrascript_aws_client.py +200 -116
  382. reconcile/utils/three_way_diff_strategy.py +1 -1
  383. reconcile/utils/unleash/server.py +2 -8
  384. reconcile/utils/vault.py +44 -41
  385. reconcile/utils/vcs.py +8 -8
  386. reconcile/vault_replication.py +119 -58
  387. tools/app_interface_reporter.py +4 -4
  388. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  389. tools/cli_commands/cost_report/view.py +7 -6
  390. tools/cli_commands/erv2.py +1 -1
  391. tools/cli_commands/gpg_encrypt.py +4 -1
  392. tools/cli_commands/systems_and_tools.py +5 -1
  393. tools/qontract_cli.py +36 -21
  394. tools/template_validation.py +3 -1
  395. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  396. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  397. reconcile/jenkins/__init__.py +0 -0
  398. reconcile/jenkins/types.py +0 -77
  399. {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
  400. {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.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,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  from reconcile.utils.mr.app_interface_reporter import CreateAppInterfaceReporter
2
4
  from reconcile.utils.mr.aws_access import CreateDeleteAwsAccessKey
3
5
  from reconcile.utils.mr.base import (
@@ -44,7 +46,7 @@ class UnknownMergeRequestTypeError(Exception):
44
46
  """
45
47
 
46
48
 
47
- def init_from_sqs_message(message) -> MergeRequestBase:
49
+ def init_from_sqs_message(message: dict[str, Any]) -> MergeRequestBase:
48
50
  # First, let's find the classes that are inheriting from
49
51
  # MergeRequestBase and create a map where the class.name is
50
52
  # the key and the class itself is the value.
@@ -1,8 +1,9 @@
1
- from datetime import datetime
1
+ from collections.abc import Iterable
2
2
  from pathlib import Path
3
3
 
4
4
  from ruamel.yaml.scalarstring import PreservedScalarString
5
5
 
6
+ from reconcile.utils.datetime_util import utc_now
6
7
  from reconcile.utils.gitlab_api import GitLabApi
7
8
  from reconcile.utils.mr.base import (
8
9
  MergeRequestBase,
@@ -14,7 +15,9 @@ from reconcile.utils.mr.labels import AUTO_MERGE
14
15
  class CreateAppInterfaceReporter(MergeRequestBase):
15
16
  name = "create_app_interface_reporter_mr"
16
17
 
17
- def __init__(self, reports, email_body, reports_path):
18
+ def __init__(
19
+ self, reports: Iterable[dict[str, str]], email_body: str, reports_path: str
20
+ ) -> None:
18
21
  self.reports = reports
19
22
  self.email_body = email_body
20
23
  self.reports_path = reports_path
@@ -23,7 +26,7 @@ class CreateAppInterfaceReporter(MergeRequestBase):
23
26
 
24
27
  self.labels = [AUTO_MERGE]
25
28
 
26
- now = datetime.now()
29
+ now = utc_now()
27
30
  self.isodate = now.isoformat()
28
31
  self.ts = now.strftime("%Y%m%d%H%M%S")
29
32
 
@@ -18,7 +18,7 @@ BODY_TEMPLATE = PROJ_ROOT / "templates" / "aws_access_key_email.j2"
18
18
  class CreateDeleteAwsAccessKey(MergeRequestBase):
19
19
  name = "create_delete_aws_access_key_mr"
20
20
 
21
- def __init__(self, account, path, key):
21
+ def __init__(self, account: str, path: str, key: str):
22
22
  self.account = account
23
23
  self.path = path.lstrip("/")
24
24
  self.key = key
@@ -1,4 +1,3 @@
1
- import json
2
1
  import logging
3
2
  from abc import (
4
3
  ABC,
@@ -11,10 +10,10 @@ from uuid import uuid4
11
10
  from gitlab.exceptions import GitlabError
12
11
  from jinja2 import Template
13
12
 
14
- from reconcile import typed_queries
15
13
  from reconcile.gql_definitions.fragments.user import User
16
14
  from reconcile.utils.constants import PROJ_ROOT
17
15
  from reconcile.utils.gitlab_api import GitLabApi
16
+ from reconcile.utils.json import json_dumps
18
17
  from reconcile.utils.mr.labels import DO_NOT_MERGE_HOLD
19
18
  from reconcile.utils.sqs_gateway import SQSGateway
20
19
 
@@ -45,14 +44,14 @@ class MergeRequestBase(ABC):
45
44
 
46
45
  name = "merge-request-base"
47
46
 
48
- def __init__(self):
47
+ def __init__(self) -> None:
49
48
  # Let's first get all the attributes from the instance
50
49
  # and use for the SQS Msg payload. With that, the msg
51
50
  # to the SQS is enough to create a new, similar, instance
52
51
  # of the child class.
53
52
  self.sqs_msg_data = {**self.__dict__}
54
53
 
55
- self.labels = [DO_NOT_MERGE_HOLD]
54
+ self.labels: Iterable[str] = [DO_NOT_MERGE_HOLD]
56
55
 
57
56
  random_id = str(uuid4())[:6]
58
57
  self.branch = f"{self.name}-{random_id}"
@@ -110,19 +109,14 @@ class MergeRequestBase(ABC):
110
109
  }
111
110
 
112
111
  def infer_author(
113
- self, author_email: str | None, all_users: Iterable[User] | None = None
112
+ self, github_user_id: str | None, all_users: Iterable[User] | None = None
114
113
  ) -> str | None:
115
- if not author_email:
114
+ if not github_user_id:
116
115
  return None
117
116
  if not all_users:
118
117
  return None
119
118
 
120
- username = author_email.split("@")[0]
121
- users = None
122
- if author_email.endswith(typed_queries.smtp.settings().mail_address):
123
- users = [u for u in all_users if username == u.org_username]
124
- elif author_email.endswith("users.noreply.github.com"):
125
- users = [u for u in all_users if username == u.github_username]
119
+ users = [u for u in all_users if u.github_username == github_user_id]
126
120
 
127
121
  if users:
128
122
  return users[0].org_username
@@ -199,7 +193,7 @@ class MergeRequestBase(ABC):
199
193
  # logging this exception
200
194
  raise MergeRequestProcessingError(
201
195
  f"error processing {self.name} changes "
202
- f"{json.dumps(self.sqs_msg_data)} "
196
+ f"{json_dumps(self.sqs_msg_data)} "
203
197
  f"into temporary branch {self.branch}. "
204
198
  f"Reason: {err}"
205
199
  ) from err
@@ -1,4 +1,6 @@
1
+ from collections.abc import Mapping
1
2
  from io import StringIO
3
+ from typing import Any
2
4
 
3
5
  from ruamel.yaml import YAML
4
6
 
@@ -15,7 +17,7 @@ yaml.width = 4096
15
17
  class CreateClustersUpdates(MergeRequestBase):
16
18
  name = "create_clusters_updates_mr"
17
19
 
18
- def __init__(self, clusters_updates):
20
+ def __init__(self, clusters_updates: Mapping[str, dict[str, Any]]):
19
21
  self.clusters_updates = clusters_updates
20
22
 
21
23
  super().__init__()
@@ -30,7 +32,7 @@ class CreateClustersUpdates(MergeRequestBase):
30
32
  def description(self) -> str:
31
33
  return DecisionCommand.APPROVED.value
32
34
 
33
- def process(self, gitlab_cli: GitLabApi):
35
+ def process(self, gitlab_cli: GitLabApi) -> None:
34
36
  changes = False
35
37
  for cluster_name, cluster_updates in self.clusters_updates.items():
36
38
  if not cluster_updates:
@@ -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,3 +1,6 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any
3
+
1
4
  from ruamel import yaml
2
5
 
3
6
  from reconcile.utils.gitlab_api import GitLabApi
@@ -8,7 +11,7 @@ from reconcile.utils.mr.labels import AUTO_MERGE
8
11
  class CreateOCMUpgradeSchedulerOrgUpdates(MergeRequestBase):
9
12
  name = "create_ocm_upgrade_scheduler_org_updates_mr"
10
13
 
11
- def __init__(self, updates_info):
14
+ def __init__(self, updates_info: Mapping[str, Any]):
12
15
  self.updates_info = updates_info
13
16
 
14
17
  super().__init__()