qontract-reconcile 0.10.2.dev394__py3-none-any.whl → 0.10.2.dev414__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 (308) hide show
  1. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +4 -3
  2. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +308 -308
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +15 -12
  5. reconcile/aus/base.py +9 -13
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +1 -1
  8. reconcile/automated_actions/config/integration.py +15 -3
  9. reconcile/aws_account_manager/integration.py +6 -6
  10. reconcile/aws_account_manager/reconciler.py +3 -3
  11. reconcile/aws_ami_cleanup/integration.py +2 -5
  12. reconcile/aws_ami_share.py +69 -62
  13. reconcile/aws_saml_idp/integration.py +5 -3
  14. reconcile/aws_saml_roles/integration.py +23 -22
  15. reconcile/aws_version_sync/integration.py +6 -12
  16. reconcile/change_owners/bundle.py +3 -3
  17. reconcile/change_owners/change_log_tracking.py +3 -2
  18. reconcile/change_owners/change_owners.py +1 -1
  19. reconcile/dashdotdb_dora.py +1 -1
  20. reconcile/dashdotdb_slo.py +1 -1
  21. reconcile/database_access_manager.py +8 -9
  22. reconcile/dynatrace_token_provider/integration.py +1 -1
  23. reconcile/endpoints_discovery/integration.py +4 -1
  24. reconcile/endpoints_discovery/merge_request.py +1 -1
  25. reconcile/endpoints_discovery/merge_request_manager.py +1 -1
  26. reconcile/external_resources/integration.py +1 -1
  27. reconcile/external_resources/manager.py +19 -7
  28. reconcile/external_resources/metrics.py +1 -1
  29. reconcile/external_resources/model.py +6 -6
  30. reconcile/external_resources/reconciler.py +7 -4
  31. reconcile/external_resources/secrets_sync.py +2 -2
  32. reconcile/external_resources/state.py +56 -14
  33. reconcile/fleet_labeler/integration.py +1 -1
  34. reconcile/gcp_image_mirror.py +2 -2
  35. reconcile/github_org.py +1 -1
  36. reconcile/github_owners.py +4 -0
  37. reconcile/gitlab_members.py +6 -12
  38. reconcile/gitlab_permissions.py +8 -12
  39. reconcile/glitchtip_project_alerts/integration.py +3 -1
  40. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  41. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  42. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  43. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  44. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  45. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  46. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  47. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  48. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  49. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
  50. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +5 -5
  51. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +5 -5
  52. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +5 -5
  53. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +5 -5
  54. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  55. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  56. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  57. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  58. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  59. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  60. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  61. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  62. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  63. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  64. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  65. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  66. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  67. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  68. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  69. reconcile/gql_definitions/common/apps.py +5 -5
  70. reconcile/gql_definitions/common/aws_vpc_requests.py +5 -5
  71. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  72. reconcile/gql_definitions/common/clusters.py +5 -5
  73. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  74. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  75. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  76. reconcile/gql_definitions/common/github_orgs.py +5 -5
  77. reconcile/gql_definitions/common/jira_settings.py +5 -5
  78. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  79. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  80. reconcile/gql_definitions/common/namespaces.py +5 -5
  81. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  82. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  83. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  84. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  85. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  86. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  87. reconcile/gql_definitions/common/quay_instances.py +5 -5
  88. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  89. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  90. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  91. reconcile/gql_definitions/common/saas_files.py +5 -5
  92. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  93. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  94. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  95. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  96. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  97. reconcile/gql_definitions/common/users.py +5 -5
  98. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  99. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  100. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  101. reconcile/gql_definitions/cost_report/settings.py +5 -5
  102. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  103. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  104. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  105. reconcile/gql_definitions/email_sender/apps.py +5 -5
  106. reconcile/gql_definitions/email_sender/emails.py +5 -5
  107. reconcile/gql_definitions/email_sender/users.py +5 -5
  108. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  109. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  110. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  111. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +5 -5
  112. reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
  113. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  114. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  115. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  116. reconcile/gql_definitions/fragments/aws_account_common.py +5 -5
  117. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  118. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  119. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  120. reconcile/gql_definitions/fragments/aws_organization.py +5 -5
  121. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  122. reconcile/gql_definitions/fragments/aws_vpc_request.py +5 -5
  123. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  124. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  125. reconcile/gql_definitions/fragments/disable.py +5 -5
  126. reconcile/gql_definitions/fragments/email_service.py +5 -5
  127. reconcile/gql_definitions/fragments/email_user.py +5 -5
  128. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  129. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  130. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  131. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  132. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  133. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  134. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  135. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  136. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  137. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  138. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  139. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  140. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  141. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  142. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  143. reconcile/gql_definitions/fragments/user.py +5 -5
  144. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  145. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  146. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  147. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  148. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  149. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  150. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  151. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  152. reconcile/gql_definitions/integrations/integrations.py +5 -5
  153. reconcile/gql_definitions/introspection.json +231 -0
  154. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  155. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  156. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  157. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +5 -5
  158. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  159. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  160. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  161. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  162. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  163. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  164. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  165. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  166. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  167. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  168. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  169. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  170. reconcile/gql_definitions/rhcs/certs.py +5 -5
  171. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  172. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  173. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  174. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  175. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  176. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  177. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  178. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  179. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  180. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  181. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  182. reconcile/gql_definitions/status_board/status_board.py +5 -5
  183. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  184. reconcile/gql_definitions/templating/template_collection.py +5 -5
  185. reconcile/gql_definitions/templating/templates.py +5 -5
  186. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  187. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  188. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  189. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  190. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  191. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  192. reconcile/gql_definitions/terraform_init/aws_accounts.py +5 -5
  193. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  194. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  195. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +5 -5
  196. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +5 -5
  197. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  198. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  199. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  200. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
  201. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  202. reconcile/integrations_manager.py +3 -3
  203. reconcile/jenkins_worker_fleets.py +9 -8
  204. reconcile/jira_permissions_validator.py +2 -2
  205. reconcile/ldap_groups/integration.py +1 -1
  206. reconcile/ocm/types.py +35 -57
  207. reconcile/ocm_aws_infrastructure_access.py +1 -1
  208. reconcile/ocm_clusters.py +4 -4
  209. reconcile/ocm_labels/integration.py +3 -2
  210. reconcile/ocm_machine_pools.py +23 -23
  211. reconcile/openshift_base.py +53 -2
  212. reconcile/openshift_cluster_bots.py +1 -1
  213. reconcile/openshift_namespace_labels.py +1 -1
  214. reconcile/openshift_namespaces.py +97 -101
  215. reconcile/openshift_resources_base.py +6 -2
  216. reconcile/openshift_rhcs_certs.py +5 -5
  217. reconcile/openshift_rolebindings.py +7 -11
  218. reconcile/openshift_saas_deploy.py +4 -5
  219. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  220. reconcile/openshift_serviceaccount_tokens.py +2 -2
  221. reconcile/openshift_upgrade_watcher.py +1 -1
  222. reconcile/oum/labelset.py +5 -3
  223. reconcile/oum/models.py +1 -4
  224. reconcile/prometheus_rules_tester/integration.py +3 -3
  225. reconcile/quay_mirror.py +1 -1
  226. reconcile/queries.py +6 -0
  227. reconcile/rhidp/common.py +3 -5
  228. reconcile/rhidp/sso_client/base.py +1 -1
  229. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  230. reconcile/skupper_network/integration.py +2 -2
  231. reconcile/slack_usergroups.py +31 -11
  232. reconcile/status_board.py +6 -6
  233. reconcile/statuspage/atlassian.py +7 -7
  234. reconcile/statuspage/page.py +4 -9
  235. reconcile/templating/lib/rendering.py +3 -3
  236. reconcile/templating/renderer.py +2 -2
  237. reconcile/terraform_cloudflare_dns.py +3 -3
  238. reconcile/terraform_cloudflare_resources.py +5 -5
  239. reconcile/terraform_cloudflare_users.py +3 -2
  240. reconcile/terraform_init/integration.py +2 -2
  241. reconcile/terraform_repo.py +16 -12
  242. reconcile/terraform_resources.py +6 -6
  243. reconcile/terraform_tgw_attachments.py +20 -18
  244. reconcile/terraform_vpc_resources/integration.py +3 -1
  245. reconcile/typed_queries/cost_report/app_names.py +1 -1
  246. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  247. reconcile/typed_queries/saas_files.py +11 -11
  248. reconcile/typed_queries/status_board.py +2 -2
  249. reconcile/unleash_feature_toggles/integration.py +4 -2
  250. reconcile/utils/acs/base.py +6 -3
  251. reconcile/utils/acs/policies.py +2 -2
  252. reconcile/utils/aws_api.py +51 -20
  253. reconcile/utils/aws_api_typed/organization.py +4 -2
  254. reconcile/utils/deadmanssnitch_api.py +1 -1
  255. reconcile/utils/early_exit_cache.py +8 -10
  256. reconcile/utils/gitlab_api.py +7 -5
  257. reconcile/utils/glitchtip/client.py +6 -2
  258. reconcile/utils/glitchtip/models.py +25 -28
  259. reconcile/utils/gql.py +4 -7
  260. reconcile/utils/instrumented_wrappers.py +1 -1
  261. reconcile/utils/internal_groups/client.py +2 -2
  262. reconcile/utils/internal_groups/models.py +8 -17
  263. reconcile/utils/jinja2/utils.py +2 -5
  264. reconcile/utils/jobcontroller/controller.py +1 -1
  265. reconcile/utils/jobcontroller/models.py +17 -1
  266. reconcile/utils/json.py +39 -1
  267. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  268. reconcile/utils/membershipsources/models.py +16 -23
  269. reconcile/utils/membershipsources/resolver.py +4 -2
  270. reconcile/utils/merge_request_manager/merge_request_manager.py +1 -1
  271. reconcile/utils/merge_request_manager/parser.py +4 -4
  272. reconcile/utils/metrics.py +5 -5
  273. reconcile/utils/models.py +304 -82
  274. reconcile/utils/mr/notificator.py +1 -1
  275. reconcile/utils/mr/user_maintenance.py +3 -2
  276. reconcile/utils/oc.py +112 -92
  277. reconcile/utils/ocm/addons.py +0 -1
  278. reconcile/utils/ocm/base.py +17 -20
  279. reconcile/utils/ocm/cluster_groups.py +1 -1
  280. reconcile/utils/ocm/identity_providers.py +2 -2
  281. reconcile/utils/ocm/labels.py +1 -1
  282. reconcile/utils/ocm/products.py +8 -8
  283. reconcile/utils/ocm/service_log.py +1 -1
  284. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  285. reconcile/utils/openshift_resource.py +5 -0
  286. reconcile/utils/pagerduty_api.py +5 -2
  287. reconcile/utils/promotion_state.py +6 -11
  288. reconcile/utils/raw_github_api.py +1 -1
  289. reconcile/utils/rhcsv2_certs.py +1 -4
  290. reconcile/utils/runtime/integration.py +1 -1
  291. reconcile/utils/saasherder/interfaces.py +13 -20
  292. reconcile/utils/saasherder/models.py +23 -20
  293. reconcile/utils/saasherder/saasherder.py +26 -17
  294. reconcile/utils/slack_api.py +2 -2
  295. reconcile/utils/structs.py +1 -1
  296. reconcile/utils/terraform_client.py +1 -1
  297. reconcile/utils/terrascript_aws_client.py +47 -43
  298. reconcile/utils/unleash/server.py +2 -8
  299. reconcile/utils/vault.py +4 -11
  300. reconcile/utils/vcs.py +8 -8
  301. reconcile/vault_replication.py +1 -1
  302. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  303. tools/cli_commands/cost_report/view.py +7 -6
  304. tools/cli_commands/erv2.py +3 -1
  305. tools/qontract_cli.py +6 -5
  306. tools/template_validation.py +3 -1
  307. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
  308. {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
reconcile/acs_rbac.py CHANGED
@@ -65,7 +65,7 @@ class AcsRole(BaseModel):
65
65
  assignments: list[AssignmentPair]
66
66
  permission_set_name: str
67
67
  access_scope: AcsAccessScope
68
- system_default: bool | None
68
+ system_default: bool | None = None
69
69
 
70
70
  @classmethod
71
71
  def build(cls, permission: Permission, usernames: list[str]) -> Self:
@@ -151,7 +151,7 @@ class AcsRbacIntegration(QontractReconcileIntegration[NoParams]):
151
151
  for permission in role.oidc_permissions or []:
152
152
  if isinstance(permission, OidcPermissionAcsV1):
153
153
  permission_usernames[
154
- Permission(**permission.dict(by_alias=True))
154
+ Permission(**permission.model_dump(by_alias=True))
155
155
  ].append(user.org_username)
156
156
  return list(starmap(AcsRole.build, permission_usernames.items()))
157
157
 
@@ -1,13 +1,13 @@
1
1
  import logging
2
2
  from collections import defaultdict
3
3
  from datetime import timedelta
4
- from typing import Optional
4
+ from typing import Annotated, Optional
5
5
 
6
6
  from pydantic import (
7
7
  BaseModel,
8
8
  Field,
9
9
  ValidationError,
10
- validator,
10
+ field_validator,
11
11
  )
12
12
  from pydantic.dataclasses import dataclass
13
13
 
@@ -289,13 +289,16 @@ class OrganizationLabelSet(BaseModel):
289
289
 
290
290
  blocked_versions: CSV | None = Field(alias=aus_label_key("blocked-versions"))
291
291
 
292
- sector_max_parallel_upgrades: dict[str, str] = labelset_groupfield(
293
- group_prefix=aus_label_key("sector-max-parallel-upgrades.")
294
- )
292
+ sector_max_parallel_upgrades: Annotated[
293
+ dict[str, str],
294
+ labelset_groupfield(
295
+ group_prefix=aus_label_key("sector-max-parallel-upgrades.")
296
+ ),
297
+ ]
295
298
 
296
- sector_deps: dict[str, CSV] = labelset_groupfield(
297
- group_prefix=aus_label_key("sector-deps.")
298
- )
299
+ sector_deps: Annotated[
300
+ dict[str, CSV], labelset_groupfield(group_prefix=aus_label_key("sector-deps."))
301
+ ]
299
302
  """
300
303
  Each sector with dependencies is represented as a `sector-deps.<sector-name>` label
301
304
  with a CSV formatted list of dependant sectors. The custom `labelset_groupfield``
@@ -357,7 +360,7 @@ def _build_org_upgrade_spec(
357
360
  ]
358
361
 
359
362
  org_labelset = build_labelset(org_labels, OrganizationLabelSet)
360
- final_org = org.copy(deep=True)
363
+ final_org = org.model_copy(deep=True)
361
364
  final_org.blocked_versions = org_labelset.blocked_versions # type: ignore
362
365
  final_org.sectors = org_labelset.sector_dependencies()
363
366
  final_org.inherit_version_data = inherit_version_data
@@ -411,7 +414,7 @@ class ClusterUpgradePolicyLabelSet(BaseModel):
411
414
  """
412
415
 
413
416
  soak_days: int = Field(alias=aus_label_key("soak-days"), ge=0)
414
- workloads: CSV = Field(alias=aus_label_key("workloads"), csv_min_items=1)
417
+ workloads: CSV = Field(alias=aus_label_key("workloads"), min_length=1)
415
418
  schedule: str = Field(alias=aus_label_key("schedule"))
416
419
  mutexes: CSV | None = Field(alias=aus_label_key("mutexes"))
417
420
  sector: str | None = Field(alias=aus_label_key("sector"))
@@ -419,14 +422,14 @@ class ClusterUpgradePolicyLabelSet(BaseModel):
419
422
  version_gate_approvals: CSV | None = Field(
420
423
  alias=aus_label_key("version-gate-approvals")
421
424
  )
422
- _schedule_validator = validator("schedule", allow_reuse=True)(cron_validator)
425
+ _schedule_validator = field_validator("schedule")(cron_validator)
423
426
 
424
427
  def build_labels_dict(self) -> dict[str, str]:
425
428
  """
426
429
  Build a dictionary of all labels in this labelset.
427
430
  """
428
431
  labels = {}
429
- for k, v in self.dict(by_alias=True).items():
432
+ for k, v in self.model_dump(by_alias=True).items():
430
433
  if v is None:
431
434
  continue
432
435
  if isinstance(v, list):
reconcile/aus/base.py CHANGED
@@ -16,7 +16,7 @@ from typing import (
16
16
  )
17
17
 
18
18
  from croniter import croniter
19
- from pydantic import BaseModel, Extra
19
+ from pydantic import BaseModel
20
20
  from requests.exceptions import HTTPError
21
21
  from semver import VersionInfo
22
22
 
@@ -404,12 +404,12 @@ class AbstractUpgradePolicy(ABC, BaseModel):
404
404
 
405
405
  cluster: OCMCluster
406
406
 
407
- id: str | None
408
- next_run: str | None
409
- schedule: str | None
407
+ id: str | None = None
408
+ next_run: str | None = None
409
+ schedule: str | None = None
410
410
  schedule_type: str
411
411
  version: str
412
- state: str | None
412
+ state: str | None = None
413
413
 
414
414
  @abstractmethod
415
415
  def create(self, ocm_api: OCMBaseClient) -> None:
@@ -430,15 +430,12 @@ def addon_upgrade_policy_soonest_next_run() -> str:
430
430
  return to_utc_seconds_iso_format(next_run)
431
431
 
432
432
 
433
- class AddonUpgradePolicy(AbstractUpgradePolicy):
433
+ class AddonUpgradePolicy(AbstractUpgradePolicy, arbitrary_types_allowed=True):
434
434
  """Class to create and delete Addon upgrade policies in OCM"""
435
435
 
436
436
  addon_id: str
437
437
  addon_service: AddonService
438
438
 
439
- class Config:
440
- arbitrary_types_allowed = True
441
-
442
439
  def create(self, ocm_api: OCMBaseClient) -> None:
443
440
  self.addon_service.create_addon_upgrade_policy(
444
441
  ocm_api=ocm_api,
@@ -521,9 +518,10 @@ class ControlPlaneUpgradePolicy(AbstractUpgradePolicy):
521
518
 
522
519
 
523
520
  class NodePoolUpgradePolicy(AbstractUpgradePolicy):
524
- node_pool: str
525
521
  """Class to create NodePoolUpgradePolicies in OCM"""
526
522
 
523
+ node_pool: str
524
+
527
525
  def create(self, ocm_api: OCMBaseClient) -> None:
528
526
  policy = {
529
527
  "version": self.version,
@@ -550,7 +548,7 @@ class NodePoolUpgradePolicy(AbstractUpgradePolicy):
550
548
  return f"node pool upgrade policy - {remove_none_values_from_dict(details)}"
551
549
 
552
550
 
553
- class UpgradePolicyHandler(BaseModel, extra=Extra.forbid):
551
+ class UpgradePolicyHandler(BaseModel, extra="forbid"):
554
552
  """Class to handle upgrade policy actions"""
555
553
 
556
554
  action: str
@@ -1124,12 +1122,10 @@ def calculate_diff(
1124
1122
  UpgradePolicyHandler(
1125
1123
  action="create",
1126
1124
  policy=AddonUpgradePolicy(
1127
- action="create",
1128
1125
  cluster=spec.cluster,
1129
1126
  version=version,
1130
1127
  schedule_type="manual",
1131
1128
  addon_id=addon_id,
1132
- upgrade_type="ADDON",
1133
1129
  addon_service=addon_service,
1134
1130
  ),
1135
1131
  )
@@ -1,4 +1,3 @@
1
- import json
2
1
  from collections.abc import Iterable
3
2
  from datetime import datetime
4
3
  from typing import (
@@ -20,6 +19,17 @@ class WorkloadHistory(BaseModel):
20
19
  soak_days: float = 0.0
21
20
  reporting: list[str] = Field(default_factory=list)
22
21
 
22
+ def __eq__(self, value: Any) -> bool:
23
+ if isinstance(value, WorkloadHistory):
24
+ return super().__eq__(value)
25
+
26
+ if isinstance(value, dict):
27
+ return self.soak_days == value.get("soak_days", 0.0) and sorted(
28
+ self.reporting
29
+ ) == sorted(value.get("reporting", []))
30
+
31
+ return False
32
+
23
33
 
24
34
  class VersionHistory(BaseModel):
25
35
  workloads: dict[str, WorkloadHistory] = Field(default_factory=dict)
@@ -38,7 +48,7 @@ class Stats(BaseModel):
38
48
 
39
49
  min_version: str
40
50
  min_version_per_workload: dict[str, str] = Field(default_factory=dict)
41
- inherited: Optional["Stats"]
51
+ inherited: Optional["Stats"] = None
42
52
 
43
53
  def inherit(self, added: "Stats") -> None:
44
54
  """adds the provided stats to our inherited data
@@ -93,12 +103,12 @@ class VersionData(BaseModel):
93
103
  upgrade policies.
94
104
  """
95
105
 
96
- check_in: datetime | None
106
+ check_in: datetime | None = None
97
107
  versions: dict[str, VersionHistory] = Field(default_factory=dict)
98
- stats: Stats | None
108
+ stats: Stats | None = None
99
109
 
100
110
  def jsondict(self) -> dict[str, Any]:
101
- return json.loads(self.json(exclude_none=True))
111
+ return self.model_dump(mode="json", exclude_none=True)
102
112
 
103
113
  def save(self, state: State, ocm_name: str) -> None:
104
114
  state.add(ocm_name, self.jsondict(), force=True)
reconcile/aus/models.py CHANGED
@@ -232,7 +232,7 @@ class SectorConfigError(Exception):
232
232
 
233
233
  class Sector(BaseModel):
234
234
  name: str
235
- max_parallel_upgrades: str | None
235
+ max_parallel_upgrades: str | None = None
236
236
  dependencies: list[Sector] = Field(default_factory=list)
237
237
  _specs: dict[str, ClusterUpgradeSpec] = PrivateAttr(default_factory=dict)
238
238
 
@@ -20,6 +20,7 @@ from reconcile.gql_definitions.automated_actions.instance import (
20
20
  AutomatedActionExternalResourceFlushElastiCacheV1,
21
21
  AutomatedActionExternalResourceRdsRebootV1,
22
22
  AutomatedActionExternalResourceRdsSnapshotV1,
23
+ AutomatedActionOpenshiftTriggerCronjobV1,
23
24
  AutomatedActionOpenshiftWorkloadDeleteV1,
24
25
  AutomatedActionOpenshiftWorkloadRestartArgumentV1,
25
26
  AutomatedActionOpenshiftWorkloadRestartV1,
@@ -82,7 +83,7 @@ class AutomatedActionsConfigIntegration(
82
83
  query_func = gql.get_api().query
83
84
  return {
84
85
  "automated_actions_instances": [
85
- c.dict() for c in self.get_automated_actions_instances(query_func)
86
+ c.model_dump() for c in self.get_automated_actions_instances(query_func)
86
87
  ]
87
88
  }
88
89
 
@@ -167,7 +168,7 @@ class AutomatedActionsConfigIntegration(
167
168
  case AutomatedActionActionListV1():
168
169
  # no special handling needed, just dump the values
169
170
  parameters.extend(
170
- arg.dict(exclude_none=True, exclude_defaults=True)
171
+ arg.model_dump(exclude_none=True, exclude_defaults=True)
171
172
  for arg in action.action_list_arguments or []
172
173
  )
173
174
  case AutomatedActionExternalResourceFlushElastiCacheV1():
@@ -205,6 +206,17 @@ class AutomatedActionsConfigIntegration(
205
206
  "account": f"^{rds_snapshot_er.provisioner.name}$",
206
207
  "identifier": rds_snapshot_arg.identifier,
207
208
  })
209
+ case AutomatedActionOpenshiftTriggerCronjobV1():
210
+ parameters.extend(
211
+ {
212
+ # all parameter values are regexes in the OPA policy
213
+ # therefore, cluster and namespace must be fixed to the current strings
214
+ "cluster": f"^{arg.namespace.cluster.name}$",
215
+ "namespace": f"^{arg.namespace.name}$",
216
+ "cronjob": arg.cronjob,
217
+ }
218
+ for arg in action.openshift_trigger_cronjob_arguments
219
+ )
208
220
  case AutomatedActionOpenshiftWorkloadDeleteV1():
209
221
  parameters.extend(
210
222
  {
@@ -257,7 +269,7 @@ class AutomatedActionsConfigIntegration(
257
269
  {
258
270
  "users": {user.username: sorted(user.roles) for user in users},
259
271
  "roles": {
260
- role: [policy.dict() for policy in policies]
272
+ role: [policy.model_dump() for policy in policies]
261
273
  for role, policies in roles.items()
262
274
  },
263
275
  },
@@ -42,14 +42,14 @@ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
42
42
 
43
43
 
44
44
  class AwsAccountMgmtIntegrationParams(PydanticRunParams):
45
- account_name: str | None
45
+ account_name: str | None = None
46
46
  flavor: str
47
47
  organization_account_role: str = "OrganizationAccountAccessRole"
48
48
  default_tags: dict[str, str] = {}
49
49
  initial_user_name: str = "terraform"
50
50
  initial_user_policy_arn: str = "arn:aws:iam::aws:policy/AdministratorAccess"
51
51
  initial_user_secret_vault_path: str = (
52
- "app-sre-v2/creds/terraform/{account_name}/config"
52
+ "app-sre-v2/creds/terraform/{account_name}/config" # noqa: RUF027
53
53
  )
54
54
  # To avoid the accidental deletion of the resource file, explicitly set the
55
55
  # qontract.cli option in the integration extraArgs!
@@ -76,9 +76,9 @@ class AwsAccountMgmtIntegration(
76
76
  query_func, account_name=self.params.account_name
77
77
  )
78
78
  return {
79
- "payer_accounts": [account.dict() for account in payer_accounts],
79
+ "payer_accounts": [account.model_dump() for account in payer_accounts],
80
80
  "non_organization_accounts": [
81
- account.dict() for account in non_organization_accounts
81
+ account.model_dump() for account in non_organization_accounts
82
82
  ],
83
83
  }
84
84
 
@@ -98,7 +98,7 @@ class AwsAccountMgmtIntegration(
98
98
  lstrip_blocks=True,
99
99
  keep_trailing_newline=True,
100
100
  ).render({
101
- "accountRequest": account_request.dict(by_alias=True),
101
+ "accountRequest": account_request.model_dump(by_alias=True),
102
102
  "uid": uid,
103
103
  "settings": settings,
104
104
  "timestamp": int(utc_now().timestamp()),
@@ -187,7 +187,7 @@ class AwsAccountMgmtIntegration(
187
187
  template=account_template,
188
188
  account_request=account_request,
189
189
  uid=uid,
190
- settings=self.params.dict(),
190
+ settings=self.params.model_dump(),
191
191
  ),
192
192
  account_request_file_path=f"data/{account_request.path.strip('/')}",
193
193
  )
@@ -35,7 +35,7 @@ class Quota(Protocol):
35
35
  quota_code: str
36
36
  value: float
37
37
 
38
- def dict(self) -> dict[str, Any]: ...
38
+ def model_dump(self) -> dict[str, Any]: ...
39
39
 
40
40
 
41
41
  class Contact(Protocol):
@@ -44,7 +44,7 @@ class Contact(Protocol):
44
44
  email: str
45
45
  phone_number: str
46
46
 
47
- def dict(self) -> dict[str, Any]: ...
47
+ def model_dump(self) -> dict[str, Any]: ...
48
48
 
49
49
 
50
50
  class AWSReconciler:
@@ -167,7 +167,7 @@ class AWSReconciler:
167
167
  self, aws_api: AWSApi, name: str, quotas: Iterable[Quota]
168
168
  ) -> list[str] | None:
169
169
  """Request service quota changes."""
170
- quotas_dict = [q.dict() for q in quotas]
170
+ quotas_dict = [q.model_dump() for q in quotas]
171
171
  with self.state.transaction(
172
172
  state_key(name, TASK_REQUEST_SERVICE_QUOTA)
173
173
  ) as _state:
@@ -40,15 +40,12 @@ QONTRACT_INTEGRATION = "aws_ami_cleanup"
40
40
  MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
41
41
 
42
42
 
43
- class AWSAmi(BaseModel):
43
+ class AWSAmi(BaseModel, frozen=True):
44
44
  name: str
45
45
  image_id: str
46
46
  creation_date: datetime
47
47
  snapshot_ids: list[str]
48
48
 
49
- class Config:
50
- frozen = True
51
-
52
49
 
53
50
  def get_aws_amis_from_launch_templates(ec2_client: EC2Client) -> set[str]:
54
51
  amis = set()
@@ -176,7 +173,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
176
173
  # Build AWSApi object. We will use all those accounts listed in ami_accounts since
177
174
  # we will also need to look for used AMIs.
178
175
  accounts_dicted = [
179
- account.dict(by_alias=True)
176
+ account.model_dump(by_alias=True)
180
177
  for account in aws_accounts or []
181
178
  if account.name in ami_accounts
182
179
  ]
@@ -1,17 +1,19 @@
1
1
  import logging
2
+ import re
2
3
  from collections.abc import (
3
- Callable,
4
4
  Iterable,
5
5
  Mapping,
6
6
  )
7
7
  from typing import Any
8
8
 
9
9
  from reconcile import queries
10
+ from reconcile.typed_queries.aws_account_tags import get_aws_account_tags
11
+ from reconcile.typed_queries.external_resources import get_settings
10
12
  from reconcile.utils.aws_api import AWSApi
11
- from reconcile.utils.defer import defer
12
13
 
13
14
  QONTRACT_INTEGRATION = "aws-ami-share"
14
- MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
15
+
16
+ MANAGED_TAG = {"managed_by_integration": QONTRACT_INTEGRATION}
15
17
 
16
18
 
17
19
  def filter_accounts(accounts: Iterable[dict[str, Any]]) -> list[dict[str, Any]]:
@@ -37,65 +39,70 @@ def get_region(
37
39
  return region
38
40
 
39
41
 
40
- @defer
41
- def run(dry_run: bool, defer: Callable | None = None) -> None:
42
+ def share_ami(
43
+ dry_run: bool,
44
+ src_account: Mapping[str, Any],
45
+ share: Mapping[str, Any],
46
+ default_tags: dict[str, str],
47
+ aws_api: AWSApi,
48
+ ) -> None:
49
+ dst_account = share["account"]
50
+ regex = re.compile(share["regex"])
51
+ region = get_region(share, src_account, dst_account)
52
+ src_amis = aws_api.get_amis_details(src_account, src_account, regex, region)
53
+ dst_amis = aws_api.get_amis_details(dst_account, src_account, regex, region)
54
+
55
+ for ami_id, src_ami_tags in src_amis.items():
56
+ dst_ami_tags = dst_amis.get(ami_id)
57
+ if dst_ami_tags is None:
58
+ logging.info([
59
+ "share_ami",
60
+ src_account["name"],
61
+ dst_account["name"],
62
+ ami_id,
63
+ ])
64
+ if not dry_run:
65
+ aws_api.share_ami(src_account, dst_account["uid"], ami_id, region)
66
+ dst_account_tags = default_tags | get_aws_account_tags(
67
+ dst_account.get("organization", None)
68
+ )
69
+ desired_tags = src_ami_tags | dst_account_tags | MANAGED_TAG
70
+ current_tags = dst_ami_tags or {}
71
+
72
+ if desired_tags != current_tags:
73
+ logging.info([
74
+ "tag_shared_ami",
75
+ dst_account["name"],
76
+ ami_id,
77
+ desired_tags,
78
+ ])
79
+ if not dry_run:
80
+ aws_api.create_tags(dst_account, ami_id, desired_tags)
81
+
82
+
83
+ def run(dry_run: bool) -> None:
42
84
  accounts = queries.get_aws_accounts(sharing=True)
43
85
  sharing_accounts = filter_accounts(accounts)
44
86
  settings = queries.get_app_interface_settings()
45
- aws_api = AWSApi(1, sharing_accounts, settings=settings, init_users=False)
46
- if defer:
47
- defer(aws_api.cleanup)
48
-
49
- for src_account in sharing_accounts:
50
- sharing = src_account.get("sharing")
51
- if not sharing:
52
- continue
53
- for share in sharing:
54
- if share["provider"] != "ami":
55
- continue
56
- dst_account = share["account"]
57
- regex = share["regex"]
58
- region = get_region(share, src_account, dst_account)
59
- src_amis = aws_api.get_amis_details(src_account, src_account, regex, region)
60
- dst_amis = aws_api.get_amis_details(dst_account, src_account, regex, region)
61
-
62
- for src_ami in src_amis:
63
- src_ami_id = src_ami["image_id"]
64
- found_dst_amis = [d for d in dst_amis if d["image_id"] == src_ami_id]
65
- if not found_dst_amis:
66
- logging.info([
67
- "share_ami",
68
- src_account["name"],
69
- dst_account["name"],
70
- src_ami_id,
71
- ])
72
- if not dry_run:
73
- aws_api.share_ami(
74
- src_account, dst_account["uid"], src_ami_id, region
75
- )
76
- # we assume an unshared ami does not have tags
77
- found_dst_amis = [{"image_id": src_ami_id, "tags": []}]
78
-
79
- dst_ami = found_dst_amis[0]
80
- dst_ami_id = dst_ami["image_id"]
81
- dst_ami_tags = dst_ami["tags"]
82
- if MANAGED_TAG not in dst_ami_tags:
83
- logging.info([
84
- "tag_shared_ami",
85
- dst_account["name"],
86
- dst_ami_id,
87
- MANAGED_TAG,
88
- ])
89
- if not dry_run:
90
- aws_api.create_tag(dst_account, dst_ami_id, MANAGED_TAG)
91
- src_ami_tags = src_ami["tags"]
92
- for src_tag in src_ami_tags:
93
- if src_tag not in dst_ami_tags:
94
- logging.info([
95
- "tag_shared_ami",
96
- dst_account["name"],
97
- dst_ami_id,
98
- src_tag,
99
- ])
100
- if not dry_run:
101
- aws_api.create_tag(dst_account, dst_ami_id, src_tag)
87
+ try:
88
+ default_tags = get_settings().default_tags
89
+ except ValueError:
90
+ # no external resources settings found
91
+ default_tags = {}
92
+
93
+ with AWSApi(
94
+ 1,
95
+ sharing_accounts,
96
+ settings=settings,
97
+ init_users=False,
98
+ ) as aws_api:
99
+ for src_account in sharing_accounts:
100
+ for share in src_account.get("sharing") or []:
101
+ if share["provider"] == "ami":
102
+ share_ami(
103
+ dry_run=dry_run,
104
+ src_account=src_account,
105
+ share=share,
106
+ default_tags=default_tags,
107
+ aws_api=aws_api,
108
+ )
@@ -82,7 +82,7 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
82
82
  if not query_func:
83
83
  query_func = gql.get_api().query
84
84
  return {
85
- "accounts": [c.dict() for c in self.get_aws_accounts(query_func)],
85
+ "accounts": [c.model_dump() for c in self.get_aws_accounts(query_func)],
86
86
  }
87
87
 
88
88
  def get_aws_accounts(
@@ -125,7 +125,9 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
125
125
  aws_accounts = self.get_aws_accounts(
126
126
  gql_api.query, account_name=self.params.account_name
127
127
  )
128
- aws_accounts_dict = [account.dict(by_alias=True) for account in aws_accounts]
128
+ aws_accounts_dict = [
129
+ account.model_dump(by_alias=True) for account in aws_accounts
130
+ ]
129
131
  try:
130
132
  default_tags = get_settings().default_tags
131
133
  except ValueError:
@@ -143,7 +145,7 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
143
145
  for saml_idp_config in self.build_saml_idp_config(
144
146
  aws_accounts,
145
147
  saml_idp_name=self.params.saml_idp_name,
146
- saml_metadata=self.get_saml_metadata(self.params.saml_metadata_url),
148
+ saml_metadata=self.get_saml_metadata(str(self.params.saml_metadata_url)),
147
149
  ):
148
150
  ts.populate_saml_idp(
149
151
  account_name=saml_idp_config.account_name,
@@ -6,10 +6,11 @@ from collections.abc import (
6
6
  )
7
7
  from typing import (
8
8
  Any,
9
+ Self,
9
10
  TypedDict,
10
11
  )
11
12
 
12
- from pydantic import BaseModel, root_validator, validator
13
+ from pydantic import BaseModel, field_validator, model_validator
13
14
 
14
15
  from reconcile.gql_definitions.aws_saml_roles.aws_accounts import (
15
16
  AWSAccountV1,
@@ -59,7 +60,8 @@ class AwsSamlRolesIntegrationParams(PydanticRunParams):
59
60
  extended_early_exit_cache_ttl_seconds: int = 3600
60
61
  log_cached_log_output: bool = False
61
62
 
62
- @validator("max_session_duration_hours")
63
+ @field_validator("max_session_duration_hours")
64
+ @classmethod
63
65
  def max_session_duration_range(cls, v: str | int) -> int:
64
66
  if 1 <= int(v) <= 12:
65
67
  return int(v)
@@ -70,7 +72,8 @@ class CustomPolicy(BaseModel):
70
72
  name: str
71
73
  policy: dict[str, Any]
72
74
 
73
- @validator("name")
75
+ @field_validator("name")
76
+ @classmethod
74
77
  def name_size(cls, v: str) -> str:
75
78
  """Check the policy name size.
76
79
 
@@ -82,7 +85,8 @@ class CustomPolicy(BaseModel):
82
85
  )
83
86
  return v
84
87
 
85
- @validator("policy")
88
+ @field_validator("policy")
89
+ @classmethod
86
90
  def policy_size(cls, v: dict[str, Any]) -> dict[str, Any]:
87
91
  """Check the policy size.
88
92
 
@@ -105,7 +109,8 @@ class AwsRole(BaseModel):
105
109
  custom_policies: list[CustomPolicy]
106
110
  managed_policies: list[ManagedPolicy]
107
111
 
108
- @validator("name")
112
+ @field_validator("name")
113
+ @classmethod
109
114
  def name_size(cls, v: str) -> str:
110
115
  """Check the role name size.
111
116
 
@@ -117,29 +122,23 @@ class AwsRole(BaseModel):
117
122
  )
118
123
  return v
119
124
 
120
- @root_validator
121
- def validate_policies(cls, values: dict[str, Any]) -> dict[str, Any]:
125
+ @model_validator(mode="after")
126
+ def validate_policies(self) -> Self:
122
127
  """Check the policies.
123
128
 
124
129
  See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
125
130
  """
126
- custom_policies = values.get("custom_policies", [])
127
- managed_policies = values.get("managed_policies", [])
128
- if len(custom_policies) + len(managed_policies) > 20:
131
+ if len(self.custom_policies) + len(self.managed_policies) > 20:
129
132
  raise ValueError(
130
- f"The role '{values['name']}' has too many policies. AWS roles can have at most 20 policies (via quota increase). Please consider consolidating the policies."
133
+ f"The role '{self.name}' has too many policies. AWS roles can have at most 20 policies (via quota increase). Please consider consolidating the policies."
131
134
  )
132
- cp_names = [cp.name for cp in custom_policies]
135
+ cp_names = [cp.name for cp in self.custom_policies]
133
136
  if len(set(cp_names)) != len(cp_names):
134
- raise ValueError(
135
- f"The role '{values['name']}' has duplicate custom policies."
136
- )
137
- mp_names = [mp.name for mp in managed_policies]
137
+ raise ValueError(f"The role '{self.name}' has duplicate custom policies.")
138
+ mp_names = [mp.name for mp in self.managed_policies]
138
139
  if len(set(mp_names)) != len(mp_names):
139
- raise ValueError(
140
- f"The role '{values['name']}' has duplicate managed policies."
141
- )
142
- return values
140
+ raise ValueError(f"The role '{self.name}' has duplicate managed policies.")
141
+ return self
143
142
 
144
143
 
145
144
  class RunnerParams(TypedDict):
@@ -164,7 +163,7 @@ class AwsSamlRolesIntegration(
164
163
  if not query_func:
165
164
  query_func = gql.get_api().query
166
165
  return {
167
- "roles": [c.dict() for c in self.get_roles(query_func)],
166
+ "roles": [c.model_dump() for c in self.get_roles(query_func)],
168
167
  }
169
168
 
170
169
  def get_aws_accounts(
@@ -252,7 +251,9 @@ class AwsSamlRolesIntegration(
252
251
  aws_accounts = self.get_aws_accounts(
253
252
  gql_api.query, account_name=self.params.account_name
254
253
  )
255
- aws_accounts_dict = [account.dict(by_alias=True) for account in aws_accounts]
254
+ aws_accounts_dict = [
255
+ account.model_dump(by_alias=True) for account in aws_accounts
256
+ ]
256
257
  aws_roles = self.get_roles(gql_api.query, account_name=self.params.account_name)
257
258
  try:
258
259
  default_tags = get_settings().default_tags