qontract-reconcile 0.10.2.dev299__py3-none-any.whl → 0.10.2.dev430__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 (403) hide show
  1. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +399 -394
  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 +4 -4
  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 +8 -11
  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 +23 -10
  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 +492 -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 +10 -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 +3050 -1393
  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 +448 -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 +5 -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 +4 -0
  270. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  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 +27 -19
  282. reconcile/terraform_users.py +29 -21
  283. reconcile/terraform_vpc_peerings.py +16 -4
  284. reconcile/terraform_vpc_resources/integration.py +32 -2
  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 +59 -43
  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 +27 -20
  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 +4 -4
  380. reconcile/utils/terraform_client.py +32 -29
  381. reconcile/utils/terrascript_aws_client.py +658 -480
  382. reconcile/utils/three_way_diff_strategy.py +1 -1
  383. reconcile/utils/throughput.py +1 -1
  384. reconcile/utils/unleash/server.py +2 -8
  385. reconcile/utils/vault.py +44 -41
  386. reconcile/utils/vcs.py +8 -8
  387. reconcile/vault_replication.py +119 -58
  388. reconcile/vpc_peerings_validator.py +2 -2
  389. tools/app_interface_reporter.py +4 -4
  390. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  391. tools/cli_commands/cost_report/view.py +7 -6
  392. tools/cli_commands/erv2.py +1 -1
  393. tools/cli_commands/gpg_encrypt.py +4 -1
  394. tools/cli_commands/systems_and_tools.py +5 -1
  395. tools/qontract_cli.py +36 -21
  396. tools/sre_checkpoints/util.py +5 -3
  397. tools/template_validation.py +3 -1
  398. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  399. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  400. reconcile/jenkins/__init__.py +0 -0
  401. reconcile/jenkins/types.py +0 -77
  402. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
  403. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  import difflib
2
2
  import filecmp
3
- import json
4
3
  import logging
5
4
  import os
6
5
  import re
@@ -8,7 +7,9 @@ import shutil
8
7
  import subprocess
9
8
  import tempfile
10
9
  import xml.etree.ElementTree as ET
10
+ from collections.abc import Iterable, Mapping
11
11
  from os import path
12
+ from pathlib import Path
12
13
  from subprocess import (
13
14
  PIPE,
14
15
  STDOUT,
@@ -17,14 +18,16 @@ from subprocess import (
17
18
  from typing import Any
18
19
 
19
20
  import yaml
20
- from jenkins_jobs.builder import JenkinsManager
21
21
  from jenkins_jobs.errors import JenkinsJobsException
22
- from jenkins_jobs.parser import YamlParser
23
- from jenkins_jobs.registry import ModuleRegistry
22
+ from jenkins_jobs.loader import load_files
23
+ from jenkins_jobs.roots import Roots
24
24
  from sretoolbox.utils import retry
25
25
 
26
26
  from reconcile.utils import throughput
27
27
  from reconcile.utils.helpers import toggle_logger
28
+ from reconcile.utils.json import json_dumps
29
+ from reconcile.utils.secret_reader import SecretReaderBase
30
+ from reconcile.utils.state import State
28
31
  from reconcile.utils.vcs import GITHUB_BASE_URL
29
32
 
30
33
  JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
@@ -33,14 +36,22 @@ JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
33
36
  class JJB:
34
37
  """Wrapper around Jenkins Jobs"""
35
38
 
36
- def __init__(self, configs, ssl_verify=True, secret_reader=None, print_only=False):
39
+ def __init__(
40
+ self,
41
+ configs: list[dict[str, Any]],
42
+ ssl_verify: bool = True,
43
+ secret_reader: SecretReaderBase | None = None,
44
+ print_only: bool = False,
45
+ ) -> None:
37
46
  self.print_only = print_only
38
47
  self.secret_reader = secret_reader
48
+ if not self.print_only and self.secret_reader is None:
49
+ raise ValueError("secret_reader must be provided if print_only is False")
39
50
  self.collect_configs(configs)
40
51
  self.modify_logger()
41
52
  self.python_https_verify = str(int(ssl_verify))
42
53
 
43
- def collect_configs(self, configs):
54
+ def collect_configs(self, configs: list[dict[str, Any]]) -> None:
44
55
  instances = {
45
56
  c["instance"]["name"]: {
46
57
  "serverUrl": c["instance"]["serverUrl"],
@@ -57,7 +68,7 @@ class JJB:
57
68
  server_url = data["serverUrl"]
58
69
  wd = tempfile.mkdtemp()
59
70
  ini = JJB_INI
60
- if not self.print_only:
71
+ if not self.print_only and self.secret_reader:
61
72
  ini = self.secret_reader.read(token)
62
73
  ini = ini.replace('"', "")
63
74
  ini = ini.replace("false", "False")
@@ -92,7 +103,7 @@ class JJB:
92
103
  self.instance_urls = instance_urls
93
104
  self.working_dirs = working_dirs
94
105
 
95
- def overwrite_configs(self, configs):
106
+ def overwrite_configs(self, configs: Mapping[str, str] | State) -> None:
96
107
  """This function will override the existing
97
108
  config files in the working directories with
98
109
  the supplied configs"""
@@ -101,12 +112,12 @@ class JJB:
101
112
  with open(config_path, "w", encoding="locale") as f:
102
113
  f.write(configs[name])
103
114
 
104
- def sort(self, configs):
115
+ def sort(self, configs: list[dict[str, Any]]) -> None:
105
116
  configs.sort(key=self.sort_by_name)
106
- configs.sort(key=self.sort_by_type)
117
+ configs.sort(key=lambda x: self.sort_by_type(x) or 0)
107
118
 
108
119
  @staticmethod
109
- def sort_by_type(config):
120
+ def sort_by_type(config: Mapping[str, Any]) -> int:
110
121
  if config["type"] == "defaults":
111
122
  return 0
112
123
  if config["type"] == "global-defaults":
@@ -123,12 +134,13 @@ class JJB:
123
134
  return 40
124
135
  if config["type"] == "jobs":
125
136
  return 50
137
+ return 100
126
138
 
127
139
  @staticmethod
128
- def sort_by_name(config):
140
+ def sort_by_name(config: Mapping[str, Any]) -> str:
129
141
  return config["name"]
130
142
 
131
- def get_configs(self):
143
+ def get_configs(self) -> dict[str, str]:
132
144
  """This function gets the configs from the
133
145
  working directories"""
134
146
  configs = {}
@@ -139,7 +151,7 @@ class JJB:
139
151
 
140
152
  return configs
141
153
 
142
- def generate(self, io_dir, fetch_state):
154
+ def generate(self, io_dir: str, fetch_state: str) -> None:
143
155
  """
144
156
  Generates job definitions from JJB configs
145
157
 
@@ -163,7 +175,7 @@ class JJB:
163
175
  self.execute(args)
164
176
  throughput.change_files_ownership(io_dir)
165
177
 
166
- def print_diffs(self, io_dir, instance_name=None):
178
+ def print_diffs(self, io_dir: str, instance_name: str | None = None) -> None:
167
179
  """Print the diffs between the current and
168
180
  the desired job definitions"""
169
181
  current_path = path.join(io_dir, "jjb", "current")
@@ -179,7 +191,7 @@ class JJB:
179
191
  self.print_diff(delete, current_path, "delete")
180
192
  self.print_diff(common, desired_path, "update")
181
193
 
182
- def print_diff(self, files, replace_path, action):
194
+ def print_diff(self, files: Iterable[str], replace_path: str, action: str) -> None:
183
195
  for f in files:
184
196
  if action == "update":
185
197
  ft = self.toggle_cd(f)
@@ -210,11 +222,16 @@ class JJB:
210
222
  ]
211
223
  logging.debug("DIFF:\n" + "".join(diff))
212
224
 
213
- def compare_files(self, from_files, subtract_files, in_op=False):
225
+ def compare_files(
226
+ self,
227
+ from_files: Iterable[str],
228
+ subtract_files: Iterable[str],
229
+ in_op: bool = False,
230
+ ) -> list[str]:
214
231
  return [f for f in from_files if (self.toggle_cd(f) in subtract_files) is in_op]
215
232
 
216
233
  @staticmethod
217
- def get_files(search_path, instance_name=None):
234
+ def get_files(search_path: str, instance_name: str | None = None) -> list[str]:
218
235
  if instance_name is not None:
219
236
  search_path = path.join(search_path, instance_name)
220
237
  return [
@@ -222,7 +239,7 @@ class JJB:
222
239
  ]
223
240
 
224
241
  @staticmethod
225
- def toggle_cd(file_name):
242
+ def toggle_cd(file_name: str) -> str:
226
243
  if "desired" in file_name:
227
244
  return file_name.replace("desired", "current")
228
245
  return file_name.replace("current", "desired")
@@ -248,43 +265,40 @@ class JJB:
248
265
  raise
249
266
 
250
267
  @staticmethod
251
- def get_jjb(args):
268
+ def get_jjb(args: Iterable[str]) -> Any:
252
269
  from jenkins_jobs.cli.entry import JenkinsJobs # noqa: PLC0415
253
270
 
254
271
  return JenkinsJobs(args)
255
272
 
256
- def execute(self, args):
273
+ def execute(self, args: Iterable[str]) -> None:
257
274
  jjb = self.get_jjb(args)
258
275
  with toggle_logger():
259
276
  jjb.execute()
260
277
 
261
- def modify_logger(self):
278
+ def modify_logger(self) -> None:
262
279
  yaml.warnings({"YAMLLoadWarning": False})
263
280
  formatter = logging.Formatter("%(levelname)s: %(message)s")
264
281
  logger = logging.getLogger()
265
282
  logger.handlers[0].setFormatter(formatter)
266
283
 
267
- def cleanup(self):
284
+ def cleanup(self) -> None:
268
285
  for wd in self.working_dirs.values():
269
286
  shutil.rmtree(wd)
270
287
 
271
288
  @retry(exceptions=(JenkinsJobsException))
272
- def get_jobs(self, wd, name):
289
+ def get_jobs(self, wd: str, name: str) -> list[dict[str, Any]]:
273
290
  ini_path = f"{wd}/{name}.ini"
274
291
  config_path = f"{wd}/config.yaml"
275
292
 
276
293
  args = ["--conf", ini_path, "test", config_path]
277
294
  jjb = self.get_jjb(args)
278
- builder = JenkinsManager(jjb.jjb_config)
279
- registry = ModuleRegistry(jjb.jjb_config, builder.plugins_list)
280
- parser = YamlParser(jjb.jjb_config)
281
- parser.load_files(jjb.options.path)
282
- jobs, _ = parser.expandYaml(registry, jjb.options.names)
295
+ roots = Roots(jjb.jjb_config)
296
+ load_files(jjb.jjb_config, roots, [Path(config_path)])
297
+ job_view_data_list = roots.generate_jobs()
298
+ return [job.data for job in job_view_data_list]
283
299
 
284
- return jobs
285
-
286
- def get_job_webhooks_data(self):
287
- job_webhooks_data = {}
300
+ def get_job_webhooks_data(self) -> dict[str, list[dict[str, Any]]]:
301
+ job_webhooks_data: dict[str, list[dict[str, Any]]] = {}
288
302
  for name, wd in self.working_dirs.items():
289
303
  jobs = self.get_jobs(wd, name)
290
304
 
@@ -313,7 +327,7 @@ class JJB:
313
327
 
314
328
  return job_webhooks_data
315
329
 
316
- def get_repos(self):
330
+ def get_repos(self) -> set[str]:
317
331
  repos = set()
318
332
  for name, wd in self.working_dirs.items():
319
333
  jobs = self.get_jobs(wd, name)
@@ -325,7 +339,7 @@ class JJB:
325
339
  logging.debug(f"missing github url: {job_name}")
326
340
  return repos
327
341
 
328
- def get_admins(self):
342
+ def get_admins(self) -> set[str]:
329
343
  admins = set()
330
344
  for name, wd in self.working_dirs.items():
331
345
  jobs = self.get_jobs(wd, name)
@@ -340,15 +354,17 @@ class JJB:
340
354
  return admins
341
355
 
342
356
  @staticmethod
343
- def get_repo_url(job):
357
+ def get_repo_url(job: Mapping[str, Any]) -> str:
344
358
  repo_url_raw = job["properties"][0]["github"]["url"]
345
359
  return repo_url_raw.strip("/").replace(".git", "")
346
360
 
347
361
  @staticmethod
348
- def get_ref(job: dict) -> str:
362
+ def get_ref(job: Mapping[str, Any]) -> str:
349
363
  return job["scm"][0]["git"]["branches"][0]
350
364
 
351
- def get_all_jobs(self, job_types=None, instance_name=None) -> dict[str, list[dict]]:
365
+ def get_all_jobs(
366
+ self, job_types: Iterable[str] | None = None, instance_name: str | None = None
367
+ ) -> dict[str, list[dict[str, Any]]]:
352
368
  if job_types is None:
353
369
  job_types = []
354
370
  all_jobs: dict[str, list[dict]] = {}
@@ -366,8 +382,8 @@ class JJB:
366
382
 
367
383
  return all_jobs
368
384
 
369
- def print_jobs(self, job_name=None):
370
- all_jobs = {}
385
+ def print_jobs(self, job_name: str | None = None) -> None:
386
+ all_jobs: dict[str, list[dict[str, Any]]] = {}
371
387
  found = False
372
388
  for name, wd in self.working_dirs.items():
373
389
  logging.debug(f"getting jobs from {name}")
@@ -380,7 +396,7 @@ class JJB:
380
396
  found = True
381
397
  if not found:
382
398
  raise ValueError(f"job name {job_name} is not found")
383
- print(json.dumps(all_jobs, indent=2))
399
+ print(json_dumps(all_jobs, indent=2))
384
400
 
385
401
  def get_job_by_repo_url(self, repo_url: str, job_type: str) -> dict[str, Any]:
386
402
  for jobs in self.get_all_jobs(job_types=[job_type]).values():
@@ -394,7 +410,7 @@ class JJB:
394
410
  raise ValueError(f"job with {job_type=} and {repo_url=} not found")
395
411
 
396
412
  @staticmethod
397
- def get_trigger_phrases_regex(job: dict) -> str | None:
413
+ def get_trigger_phrases_regex(job: Mapping[str, Any]) -> str | None:
398
414
  for trigger in job["triggers"]:
399
415
  if "gitlab" in trigger:
400
416
  return trigger["gitlab"].get("note-regex")
@@ -403,7 +419,7 @@ class JJB:
403
419
  return None
404
420
 
405
421
  @staticmethod
406
- def get_gitlab_webhook_trigger(job: dict) -> list[str]:
422
+ def get_gitlab_webhook_trigger(job: Mapping[str, Any]) -> list[str]:
407
423
  gitlab_triggers = job["triggers"][0]["gitlab"]
408
424
  # pr-check job should be triggered by merge request events
409
425
  # and certain comments: [test]|/retest|/lgtm|/lgtm cancel|/hold|/hold cancel
@@ -3,7 +3,7 @@ import time
3
3
  from datetime import datetime
4
4
  from typing import Protocol, TextIO
5
5
 
6
- from kubernetes.client import ( # type: ignore[attr-defined]
6
+ from kubernetes.client import (
7
7
  ApiClient,
8
8
  V1Job,
9
9
  V1ObjectMeta,
@@ -100,7 +100,7 @@ class K8sJobController:
100
100
  """
101
101
  new_cache = {}
102
102
  for item in self.oc.get_items(
103
- kind="Job",
103
+ kind="Job.batch",
104
104
  namespace=self.namespace,
105
105
  ):
106
106
  openshift_resource = OpenshiftResource(
@@ -38,6 +38,8 @@ class JobValidationError(Exception):
38
38
 
39
39
 
40
40
  JOB_GENERATION_ANNOTATION = "qontract-reconcile/job.generation"
41
+ MAX_JOB_NAME_LENGTH = 63
42
+ UNIT_OF_WORK_DIGEST_LENGTH = 10
41
43
 
42
44
 
43
45
  class K8sJob(ABC):
@@ -72,7 +74,21 @@ class K8sJob(ABC):
72
74
  """
73
75
 
74
76
  def name(self) -> str:
75
- return f"{self.name_prefix()}-{self.unit_of_work_digest()}"
77
+ """
78
+ Generate the full job name by combining the name prefix with a digest.
79
+
80
+ The name is constructed from the name_prefix (truncated to ensure total
81
+ length compliance) and the unit_of_work_digest. The total length is
82
+ limited to MAX_JOB_NAME_LENGTH (63 characters) to comply with Kubernetes
83
+ naming constraints.
84
+
85
+ Returns:
86
+ A unique job name in the format: {name_prefix}-{digest}
87
+ """
88
+ prefix = self.name_prefix()[
89
+ : MAX_JOB_NAME_LENGTH - UNIT_OF_WORK_DIGEST_LENGTH - 1
90
+ ]
91
+ return f"{prefix}-{self.unit_of_work_digest(UNIT_OF_WORK_DIGEST_LENGTH)}"
76
92
 
77
93
  @abstractmethod
78
94
  def name_prefix(self) -> str:
@@ -0,0 +1,74 @@
1
+ import json
2
+ from collections.abc import Callable
3
+ from dataclasses import asdict, is_dataclass
4
+ from datetime import date, datetime
5
+ from decimal import Decimal
6
+ from enum import Enum
7
+ from typing import Any, Literal
8
+
9
+ from pydantic import BaseModel
10
+ from pydantic.main import IncEx
11
+
12
+ JSON_COMPACT_SEPARATORS = (",", ":")
13
+
14
+
15
+ def pydantic_encoder(obj: Any) -> Any:
16
+ if isinstance(obj, BaseModel):
17
+ return obj.model_dump()
18
+
19
+ if is_dataclass(obj):
20
+ return asdict(obj) # type: ignore
21
+
22
+ if isinstance(obj, (datetime, date)):
23
+ return obj.isoformat()
24
+
25
+ if isinstance(obj, Enum):
26
+ return obj.value
27
+
28
+ if isinstance(obj, Decimal):
29
+ return float(obj)
30
+
31
+ raise TypeError(
32
+ f"Object of type '{obj.__class__.__name__}' is not JSON serializable"
33
+ )
34
+
35
+
36
+ def json_dumps(
37
+ data: Any,
38
+ *,
39
+ compact: bool = False,
40
+ indent: int | None = None,
41
+ cls: type[json.JSONEncoder] | None = None,
42
+ defaults: Callable | None = None,
43
+ # BaseModel dump parameters
44
+ by_alias: bool = True,
45
+ exclude_none: bool = False,
46
+ exclude: IncEx | None = None,
47
+ mode: Literal["json", "python"] = "json",
48
+ ) -> str:
49
+ """
50
+ Serialize `data` to a consistent JSON formatted `str` with dict keys sorted.
51
+
52
+ Args:
53
+ data: The data to serialize.
54
+ compact: If True, use compact separators (no spaces after commas or colons).
55
+ indent: If specified, pretty-print the JSON with this many spaces of indentation.
56
+ cls: A custom JSONEncoder subclass to use for serialization.
57
+ Returns:
58
+ A JSON formatted string.
59
+ """
60
+ if isinstance(data, BaseModel):
61
+ data = data.model_dump(
62
+ mode=mode, by_alias=by_alias, exclude_none=exclude_none, exclude=exclude
63
+ )
64
+ if mode == "python":
65
+ defaults = pydantic_encoder
66
+ separators = JSON_COMPACT_SEPARATORS if compact else None
67
+ return json.dumps(
68
+ data,
69
+ indent=indent,
70
+ separators=separators,
71
+ sort_keys=True,
72
+ cls=cls,
73
+ default=defaults,
74
+ )
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Iterable
3
+ from typing import Any, Self
3
4
 
4
5
  from ldap3 import (
5
6
  ALL,
@@ -17,15 +18,15 @@ class LdapClient:
17
18
  appropriately.
18
19
  """
19
20
 
20
- def __init__(self, base_dn: str, connection: Connection):
21
+ def __init__(self, base_dn: str, connection: Connection) -> None:
21
22
  self.base_dn = base_dn
22
23
  self.connection = connection
23
24
 
24
- def __enter__(self):
25
+ def __enter__(self) -> Self:
25
26
  self.connection.bind()
26
27
  return self
27
28
 
28
- def __exit__(self, exc_type, exc_val, exc_tb):
29
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
29
30
  self.connection.unbind()
30
31
 
31
32
  def get_users(self, uids: Iterable[str]) -> set[str]:
@@ -6,7 +6,9 @@ from collections.abc import Mapping
6
6
  from typing import Any
7
7
 
8
8
 
9
- def state_rm_access_key(working_dirs, account, user):
9
+ def state_rm_access_key(
10
+ working_dirs: Mapping[str, str], account: str, user: str
11
+ ) -> bool:
10
12
  wd = working_dirs[account]
11
13
  init_result = subprocess.run(["terraform", "init"], check=False, cwd=wd)
12
14
  if init_result.returncode != 0:
@@ -55,6 +55,8 @@ def resolve_app_interface_membership_source(
55
55
 
56
56
  def build_member_list(role: RoleV1) -> list[RoleMember]:
57
57
  members: list[RoleMember] = []
58
- members.extend([RoleUser(**u.dict()) for u in role.users or []])
59
- members.extend([RoleBot(**b.dict()) for b in role.bots or [] if b.org_username])
58
+ members.extend([RoleUser(**u.model_dump()) for u in role.users or []])
59
+ members.extend([
60
+ RoleBot(**b.model_dump()) for b in role.bots or [] if b.org_username
61
+ ])
60
62
  return members
@@ -7,7 +7,6 @@ from typing import (
7
7
 
8
8
  from pydantic import (
9
9
  BaseModel,
10
- Extra,
11
10
  )
12
11
 
13
12
  from reconcile.gql_definitions.fragments.membership_source import (
@@ -23,7 +22,7 @@ class User(Protocol):
23
22
  @property
24
23
  def org_username(self) -> str: ...
25
24
 
26
- def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
25
+ def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
27
26
 
28
27
 
29
28
  class Bot(Protocol):
@@ -33,7 +32,7 @@ class Bot(Protocol):
33
32
  @property
34
33
  def org_username(self) -> str | None: ...
35
34
 
36
- def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
35
+ def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
37
36
 
38
37
 
39
38
  class RoleWithMemberships(Protocol):
@@ -50,33 +49,27 @@ class RoleWithMemberships(Protocol):
50
49
  def member_sources(self) -> Sequence[RoleMembershipSource] | None: ...
51
50
 
52
51
 
53
- class RoleUser(BaseModel):
52
+ class RoleUser(BaseModel, extra="ignore"):
54
53
  name: str
55
54
  org_username: str
56
- github_username: str | None
57
- quay_username: str | None
58
- pagerduty_username: str | None
59
- aws_username: str | None
60
- cloudflare_user: str | None
61
- public_gpg_key: str | None
55
+ github_username: str | None = None
56
+ quay_username: str | None = None
57
+ pagerduty_username: str | None = None
58
+ aws_username: str | None = None
59
+ cloudflare_user: str | None = None
60
+ public_gpg_key: str | None = None
62
61
  tag_on_cluster_updates: bool | None = False
63
62
  tag_on_merge_requests: bool | None = False
64
63
 
65
- class Config:
66
- extra = Extra.ignore
67
64
 
68
-
69
- class RoleBot(BaseModel):
65
+ class RoleBot(BaseModel, extra="ignore"):
70
66
  name: str
71
- description: str | None
72
- org_username: str | None
73
- github_username: str | None
74
- gitlab_username: str | None
75
- openshift_serviceaccount: str | None
76
- quay_username: str | None
77
-
78
- class Config:
79
- extra = Extra.ignore
67
+ description: str | None = None
68
+ org_username: str | None = None
69
+ github_username: str | None = None
70
+ gitlab_username: str | None = None
71
+ openshift_serviceaccount: str | None = None
72
+ quay_username: str | None = None
80
73
 
81
74
 
82
75
  RoleMember = RoleUser | RoleBot
@@ -98,8 +98,10 @@ def resolve_role_members(
98
98
  members: list[RoleMember] = []
99
99
 
100
100
  # bring in the local users and bots ...
101
- members.extend(RoleUser(**u.dict()) for u in r.users or [])
102
- members.extend(RoleBot(**b.dict()) for b in r.bots or [] if b.org_username)
101
+ members.extend(RoleUser(**u.model_dump()) for u in r.users or [])
102
+ members.extend(
103
+ RoleBot(**b.model_dump()) for b in r.bots or [] if b.org_username
104
+ )
103
105
 
104
106
  # ... and enhance with the ones from member sources
105
107
  for ms in r.member_sources or []:
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from abc import abstractmethod
3
3
  from dataclasses import dataclass
4
- from typing import Any, Generic, TypeVar
4
+ from typing import Any, TypeVar
5
5
 
6
6
  from gitlab.v4.objects import ProjectMergeRequest
7
7
  from pydantic import BaseModel
@@ -17,12 +17,12 @@ T = TypeVar("T", bound=BaseModel)
17
17
 
18
18
 
19
19
  @dataclass
20
- class OpenMergeRequest(Generic[T]):
20
+ class OpenMergeRequest[T: BaseModel]:
21
21
  raw: ProjectMergeRequest
22
22
  mr_info: T
23
23
 
24
24
 
25
- class MergeRequestManagerBase(Generic[T]):
25
+ class MergeRequestManagerBase[T: BaseModel]:
26
26
  """ """
27
27
 
28
28
  def __init__(self, vcs: VCS, parser: Parser, mr_label: str):
@@ -42,7 +42,7 @@ class MergeRequestManagerBase(Generic[T]):
42
42
  expected_data: dict[str, Any],
43
43
  ) -> OpenMergeRequest | None:
44
44
  for mr in self._open_mrs:
45
- mr_info_dict = mr.mr_info.dict()
45
+ mr_info_dict = mr.mr_info.model_dump()
46
46
  if all(mr_info_dict.get(k) == expected_data.get(k) for k in expected_data):
47
47
  return mr
48
48
 
@@ -1,5 +1,5 @@
1
1
  import re
2
- from typing import Generic, TypeVar
2
+ from typing import TypeVar
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
@@ -17,7 +17,7 @@ class ParserVersionError(Exception):
17
17
  T = TypeVar("T", bound=BaseModel)
18
18
 
19
19
 
20
- class Parser(Generic[T]):
20
+ class Parser[T: BaseModel]:
21
21
  """This class is only concerned with parsing an MR description rendered by the Renderer."""
22
22
 
23
23
  def __init__(
@@ -60,8 +60,8 @@ class Parser(Generic[T]):
60
60
 
61
61
  if self.expected_version != self._find_by_name(self.version_ref, parts[1]):
62
62
  raise ParserVersionError("Version is outdated")
63
- return self.klass(
64
- **data_default_none(
65
- self.klass, self._data_from_description(parts[1]), use_defaults=False
66
- )
63
+ data = data_default_none(
64
+ self.klass, self._data_from_description(parts[1]), use_defaults=False
67
65
  )
66
+ assert isinstance(data, dict)
67
+ return self.klass(**data)
@@ -144,7 +144,7 @@ class GaugeMetric(BaseMetric):
144
144
 
145
145
  @classmethod
146
146
  def metric_family(cls) -> GaugeMetricFamily:
147
- labels = [f.alias for f in cls.__fields__.values()]
147
+ labels = [f.alias or name for name, f in cls.model_fields.items()]
148
148
  return GaugeMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
149
149
 
150
150
  @classmethod
@@ -167,7 +167,7 @@ class CounterMetric(BaseMetric):
167
167
 
168
168
  @classmethod
169
169
  def metric_family(cls) -> CounterMetricFamily:
170
- labels = [f.alias for f in cls.__fields__.values()]
170
+ labels = [f.alias or name for name, f in cls.model_fields.items()]
171
171
  return CounterMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
172
172
 
173
173
  @classmethod
@@ -198,7 +198,7 @@ class MetricsContainer:
198
198
  """
199
199
  Sets the value of the given gauge metric to the given value.
200
200
  """
201
- label_values = tuple(metric.dict(by_alias=True).values())
201
+ label_values = tuple(metric.model_dump(by_alias=True).values())
202
202
  self._gauges[metric.__class__][label_values] = value
203
203
 
204
204
  def set_info(self, metric: InfoMetric) -> None:
@@ -213,7 +213,7 @@ class MetricsContainer:
213
213
  Increases the value of the given counter by the given amount.
214
214
  """
215
215
  # all label values need to be strings, so lets convert them
216
- label_values = tuple(str(v) for v in counter.dict(by_alias=True).values())
216
+ label_values = tuple(str(v) for v in counter.model_dump(by_alias=True).values())
217
217
  current_value = self._counters[counter.__class__].get(label_values) or 0
218
218
  self._counters[counter.__class__][label_values] = current_value + by
219
219
 
@@ -270,7 +270,7 @@ class MetricsContainer:
270
270
  (
271
271
  metric_class(**{
272
272
  key: labels[i]
273
- for i, key in enumerate(metric_class.__fields__.keys())
273
+ for i, key in enumerate(metric_class.model_fields.keys())
274
274
  }),
275
275
  value,
276
276
  )