qontract-reconcile 0.10.2.dev349__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 (356) hide show
  1. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +12 -11
  2. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +356 -350
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +15 -12
  5. reconcile/aus/base.py +26 -27
  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 +8 -8
  10. reconcile/aws_account_manager/reconciler.py +3 -3
  11. reconcile/aws_ami_cleanup/integration.py +8 -12
  12. reconcile/aws_ami_share.py +69 -62
  13. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  14. reconcile/aws_ecr_image_pull_secrets.py +2 -2
  15. reconcile/aws_iam_keys.py +7 -41
  16. reconcile/aws_saml_idp/integration.py +12 -4
  17. reconcile/aws_saml_roles/integration.py +32 -25
  18. reconcile/aws_version_sync/integration.py +6 -12
  19. reconcile/change_owners/bundle.py +3 -3
  20. reconcile/change_owners/change_log_tracking.py +3 -2
  21. reconcile/change_owners/change_owners.py +1 -1
  22. reconcile/change_owners/diff.py +2 -4
  23. reconcile/checkpoint.py +11 -3
  24. reconcile/cli.py +33 -8
  25. reconcile/dashdotdb_dora.py +5 -12
  26. reconcile/dashdotdb_slo.py +1 -1
  27. reconcile/database_access_manager.py +123 -117
  28. reconcile/dynatrace_token_provider/integration.py +1 -1
  29. reconcile/endpoints_discovery/integration.py +4 -1
  30. reconcile/endpoints_discovery/merge_request.py +1 -1
  31. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  32. reconcile/external_resources/factories.py +5 -12
  33. reconcile/external_resources/integration.py +1 -1
  34. reconcile/external_resources/manager.py +24 -10
  35. reconcile/external_resources/meta.py +0 -1
  36. reconcile/external_resources/metrics.py +1 -1
  37. reconcile/external_resources/model.py +13 -13
  38. reconcile/external_resources/reconciler.py +7 -4
  39. reconcile/external_resources/secrets_sync.py +6 -8
  40. reconcile/external_resources/state.py +60 -17
  41. reconcile/fleet_labeler/integration.py +1 -1
  42. reconcile/gabi_authorized_users.py +8 -5
  43. reconcile/gcp_image_mirror.py +2 -2
  44. reconcile/github_org.py +1 -1
  45. reconcile/github_owners.py +4 -0
  46. reconcile/gitlab_housekeeping.py +13 -15
  47. reconcile/gitlab_members.py +6 -12
  48. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  49. reconcile/gitlab_owners.py +15 -11
  50. reconcile/gitlab_permissions.py +8 -12
  51. reconcile/glitchtip_project_alerts/integration.py +3 -1
  52. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  53. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  54. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  55. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  56. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  57. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  58. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  59. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  60. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  61. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
  62. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  63. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  64. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  65. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  66. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  67. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  68. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  69. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  70. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  71. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  72. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  73. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  74. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  75. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  76. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  77. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  78. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  79. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  80. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  81. reconcile/gql_definitions/common/apps.py +5 -5
  82. reconcile/gql_definitions/common/aws_vpc_requests.py +15 -5
  83. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  84. reconcile/gql_definitions/common/clusters.py +7 -5
  85. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  86. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  87. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  88. reconcile/gql_definitions/common/github_orgs.py +5 -5
  89. reconcile/gql_definitions/common/jira_settings.py +5 -5
  90. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  91. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  92. reconcile/gql_definitions/common/namespaces.py +5 -5
  93. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  94. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  95. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  96. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  97. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  98. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  99. reconcile/gql_definitions/common/quay_instances.py +5 -5
  100. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  101. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  102. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  103. reconcile/gql_definitions/common/saas_files.py +5 -5
  104. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  105. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  106. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  107. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  108. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  109. reconcile/gql_definitions/common/users.py +5 -5
  110. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  111. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  112. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  113. reconcile/gql_definitions/cost_report/settings.py +5 -5
  114. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  115. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  116. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  117. reconcile/gql_definitions/email_sender/apps.py +5 -5
  118. reconcile/gql_definitions/email_sender/emails.py +5 -5
  119. reconcile/gql_definitions/email_sender/users.py +5 -5
  120. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  121. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  122. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  123. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +89 -6
  124. reconcile/gql_definitions/external_resources/external_resources_settings.py +7 -5
  125. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  126. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  127. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  128. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  129. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  130. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  131. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  132. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  133. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_vpc_request.py +7 -5
  135. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  136. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  137. reconcile/gql_definitions/fragments/disable.py +5 -5
  138. reconcile/gql_definitions/fragments/email_service.py +5 -5
  139. reconcile/gql_definitions/fragments/email_user.py +5 -5
  140. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  141. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  142. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  143. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  144. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  145. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  146. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  147. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  148. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  149. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  150. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  151. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  152. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  153. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  154. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  155. reconcile/gql_definitions/fragments/user.py +5 -5
  156. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  157. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  158. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  159. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  160. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  161. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  162. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  163. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  164. reconcile/gql_definitions/integrations/integrations.py +5 -5
  165. reconcile/gql_definitions/introspection.json +2137 -1053
  166. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  167. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  168. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  169. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  170. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  171. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  172. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  173. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  174. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  175. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  176. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  177. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  178. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  179. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  180. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  181. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  182. reconcile/gql_definitions/rhcs/certs.py +5 -5
  183. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  184. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  185. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  186. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  187. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  188. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  189. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  190. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  191. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  192. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  193. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  194. reconcile/gql_definitions/status_board/status_board.py +5 -5
  195. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  196. reconcile/gql_definitions/templating/template_collection.py +5 -5
  197. reconcile/gql_definitions/templating/templates.py +5 -5
  198. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  199. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  200. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  201. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  202. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  203. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  204. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  205. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  206. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  207. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +38 -6
  208. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  209. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  210. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  211. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  212. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
  213. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  214. reconcile/integrations_manager.py +3 -3
  215. reconcile/jenkins_worker_fleets.py +10 -8
  216. reconcile/jira_permissions_validator.py +237 -122
  217. reconcile/ldap_groups/integration.py +1 -1
  218. reconcile/ocm/types.py +35 -56
  219. reconcile/ocm_aws_infrastructure_access.py +1 -1
  220. reconcile/ocm_clusters.py +4 -4
  221. reconcile/ocm_labels/integration.py +3 -2
  222. reconcile/ocm_machine_pools.py +23 -23
  223. reconcile/openshift_base.py +53 -2
  224. reconcile/openshift_cluster_bots.py +3 -2
  225. reconcile/openshift_namespace_labels.py +1 -1
  226. reconcile/openshift_namespaces.py +97 -101
  227. reconcile/openshift_resources_base.py +6 -2
  228. reconcile/openshift_rhcs_certs.py +5 -5
  229. reconcile/openshift_rolebindings.py +7 -11
  230. reconcile/openshift_saas_deploy.py +6 -7
  231. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  232. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  233. reconcile/openshift_serviceaccount_tokens.py +2 -2
  234. reconcile/openshift_upgrade_watcher.py +4 -4
  235. reconcile/oum/labelset.py +5 -3
  236. reconcile/oum/models.py +1 -4
  237. reconcile/prometheus_rules_tester/integration.py +3 -3
  238. reconcile/quay_mirror.py +1 -1
  239. reconcile/queries.py +131 -1
  240. reconcile/rhidp/common.py +3 -5
  241. reconcile/rhidp/sso_client/base.py +1 -1
  242. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  243. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  244. reconcile/skupper_network/integration.py +2 -2
  245. reconcile/slack_usergroups.py +35 -14
  246. reconcile/sql_query.py +1 -0
  247. reconcile/status_board.py +6 -6
  248. reconcile/statuspage/atlassian.py +7 -7
  249. reconcile/statuspage/integrations/maintenances.py +4 -3
  250. reconcile/statuspage/page.py +4 -9
  251. reconcile/statuspage/status.py +5 -8
  252. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
  253. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  254. reconcile/templating/lib/rendering.py +3 -3
  255. reconcile/templating/renderer.py +4 -3
  256. reconcile/terraform_aws_route53.py +7 -1
  257. reconcile/terraform_cloudflare_dns.py +3 -3
  258. reconcile/terraform_cloudflare_resources.py +5 -5
  259. reconcile/terraform_cloudflare_users.py +3 -2
  260. reconcile/terraform_init/integration.py +187 -23
  261. reconcile/terraform_repo.py +16 -12
  262. reconcile/terraform_resources.py +17 -7
  263. reconcile/terraform_tgw_attachments.py +27 -19
  264. reconcile/terraform_users.py +7 -0
  265. reconcile/terraform_vpc_peerings.py +14 -3
  266. reconcile/terraform_vpc_resources/integration.py +10 -1
  267. reconcile/typed_queries/aws_account_tags.py +41 -0
  268. reconcile/typed_queries/cost_report/app_names.py +1 -1
  269. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  270. reconcile/typed_queries/saas_files.py +13 -13
  271. reconcile/typed_queries/status_board.py +2 -2
  272. reconcile/unleash_feature_toggles/integration.py +4 -2
  273. reconcile/utils/acs/base.py +6 -3
  274. reconcile/utils/acs/policies.py +2 -2
  275. reconcile/utils/aggregated_list.py +4 -3
  276. reconcile/utils/aws_api.py +51 -54
  277. reconcile/utils/aws_api_typed/api.py +38 -9
  278. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  279. reconcile/utils/aws_api_typed/logs.py +73 -0
  280. reconcile/utils/aws_api_typed/organization.py +4 -2
  281. reconcile/utils/datetime_util.py +67 -0
  282. reconcile/utils/deadmanssnitch_api.py +1 -1
  283. reconcile/utils/differ.py +2 -3
  284. reconcile/utils/early_exit_cache.py +11 -12
  285. reconcile/utils/expiration.py +7 -3
  286. reconcile/utils/external_resource_spec.py +24 -1
  287. reconcile/utils/filtering.py +1 -1
  288. reconcile/utils/gitlab_api.py +7 -5
  289. reconcile/utils/glitchtip/client.py +6 -2
  290. reconcile/utils/glitchtip/models.py +25 -28
  291. reconcile/utils/gql.py +4 -7
  292. reconcile/utils/helm.py +2 -1
  293. reconcile/utils/helpers.py +1 -1
  294. reconcile/utils/instrumented_wrappers.py +1 -1
  295. reconcile/utils/internal_groups/client.py +2 -2
  296. reconcile/utils/internal_groups/models.py +8 -17
  297. reconcile/utils/jinja2/utils.py +6 -101
  298. reconcile/utils/jira_client.py +82 -63
  299. reconcile/utils/jjb_client.py +9 -12
  300. reconcile/utils/jobcontroller/controller.py +1 -1
  301. reconcile/utils/jobcontroller/models.py +17 -1
  302. reconcile/utils/json.py +70 -0
  303. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  304. reconcile/utils/membershipsources/models.py +16 -23
  305. reconcile/utils/membershipsources/resolver.py +4 -2
  306. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  307. reconcile/utils/merge_request_manager/parser.py +6 -6
  308. reconcile/utils/metrics.py +5 -5
  309. reconcile/utils/models.py +304 -82
  310. reconcile/utils/mr/app_interface_reporter.py +2 -2
  311. reconcile/utils/mr/base.py +2 -2
  312. reconcile/utils/mr/notificator.py +3 -3
  313. reconcile/utils/mr/update_access_report_base.py +3 -4
  314. reconcile/utils/mr/user_maintenance.py +3 -2
  315. reconcile/utils/oc.py +118 -97
  316. reconcile/utils/oc_filters.py +3 -3
  317. reconcile/utils/ocm/addons.py +0 -1
  318. reconcile/utils/ocm/base.py +17 -20
  319. reconcile/utils/ocm/cluster_groups.py +1 -1
  320. reconcile/utils/ocm/identity_providers.py +2 -2
  321. reconcile/utils/ocm/labels.py +1 -1
  322. reconcile/utils/ocm/products.py +9 -3
  323. reconcile/utils/ocm/search_filters.py +3 -6
  324. reconcile/utils/ocm/service_log.py +4 -6
  325. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  326. reconcile/utils/openshift_resource.py +10 -5
  327. reconcile/utils/output.py +3 -2
  328. reconcile/utils/pagerduty_api.py +10 -7
  329. reconcile/utils/promotion_state.py +6 -11
  330. reconcile/utils/raw_github_api.py +1 -1
  331. reconcile/utils/rhcsv2_certs.py +1 -4
  332. reconcile/utils/runtime/integration.py +2 -3
  333. reconcile/utils/runtime/runner.py +2 -2
  334. reconcile/utils/saasherder/interfaces.py +13 -20
  335. reconcile/utils/saasherder/models.py +25 -21
  336. reconcile/utils/saasherder/saasherder.py +35 -24
  337. reconcile/utils/slack_api.py +26 -4
  338. reconcile/utils/sloth.py +171 -2
  339. reconcile/utils/sqs_gateway.py +2 -1
  340. reconcile/utils/state.py +2 -1
  341. reconcile/utils/structs.py +1 -1
  342. reconcile/utils/terraform_client.py +5 -4
  343. reconcile/utils/terrascript_aws_client.py +171 -114
  344. reconcile/utils/unleash/server.py +2 -8
  345. reconcile/utils/vault.py +5 -12
  346. reconcile/utils/vcs.py +8 -8
  347. reconcile/vault_replication.py +107 -42
  348. tools/app_interface_reporter.py +4 -4
  349. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  350. tools/cli_commands/cost_report/view.py +7 -6
  351. tools/cli_commands/erv2.py +3 -1
  352. tools/cli_commands/systems_and_tools.py +5 -1
  353. tools/qontract_cli.py +31 -18
  354. tools/template_validation.py +3 -1
  355. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
  356. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
reconcile/utils/oc.py CHANGED
@@ -12,7 +12,6 @@ import threading
12
12
  import time
13
13
  from contextlib import suppress
14
14
  from dataclasses import dataclass
15
- from datetime import datetime
16
15
  from functools import cache, wraps
17
16
  from subprocess import Popen
18
17
  from threading import Lock
@@ -47,6 +46,8 @@ from sretoolbox.utils import (
47
46
  )
48
47
 
49
48
  from reconcile.status import RunningState
49
+ from reconcile.utils.datetime_util import utc_now
50
+ from reconcile.utils.json import json_dumps
50
51
  from reconcile.utils.jump_host import (
51
52
  JumphostParameters,
52
53
  JumpHostSSH,
@@ -67,7 +68,8 @@ if TYPE_CHECKING:
67
68
  urllib3.disable_warnings()
68
69
 
69
70
  GET_REPLICASET_MAX_ATTEMPTS = 20
70
-
71
+ DEFAULT_GROUP = ""
72
+ PROJECT_KIND = "Project.project.openshift.io"
71
73
 
72
74
  oc_run_execution_counter = Counter(
73
75
  name="oc_run_execution_counter",
@@ -144,6 +146,14 @@ class RequestEntityTooLargeError(Exception):
144
146
  pass
145
147
 
146
148
 
149
+ class KindNotFoundError(Exception):
150
+ pass
151
+
152
+
153
+ class AmbiguousResourceTypeError(Exception):
154
+ pass
155
+
156
+
147
157
  class OCDecorators:
148
158
  @classmethod
149
159
  def process_reconcile_time(cls, function: Callable) -> Callable:
@@ -379,10 +389,7 @@ class OCCli:
379
389
 
380
390
  self.init_projects = init_projects
381
391
  if self.init_projects:
382
- if self.is_kind_supported("Project"):
383
- kind = "Project.project.openshift.io"
384
- else:
385
- kind = "Namespace"
392
+ kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
386
393
  self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
387
394
 
388
395
  self.slow_oc_reconcile_threshold = float(
@@ -452,10 +459,7 @@ class OCCli:
452
459
 
453
460
  self.init_projects = init_projects
454
461
  if self.init_projects:
455
- if self.is_kind_supported("Project"):
456
- kind = "Project.project.openshift.io"
457
- else:
458
- kind = "Namespace"
462
+ kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
459
463
  self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
460
464
 
461
465
  self.slow_oc_reconcile_threshold = float(
@@ -563,7 +567,7 @@ class OCCli:
563
567
  "-f",
564
568
  "-",
565
569
  ] + parameters_to_process
566
- result = self._run(cmd, stdin=json.dumps(template, sort_keys=True))
570
+ result = self._run(cmd, stdin=json_dumps(template))
567
571
  return json.loads(result)["items"]
568
572
 
569
573
  @OCDecorators.process_reconcile_time
@@ -592,7 +596,7 @@ class OCCli:
592
596
  def patch(
593
597
  self, namespace: str, kind: str, name: str, patch: Mapping[str, Any]
594
598
  ) -> OCProcessReconcileTimeDecoratorMsg:
595
- cmd = ["patch", "-n", namespace, kind, name, "-p", json.dumps(patch)]
599
+ cmd = ["patch", "-n", namespace, kind, name, "-p", json_dumps(patch)]
596
600
  self._run(cmd)
597
601
  resource = OR({"kind": kind, "metadata": {"name": name}}, "", "")
598
602
  return self._msg_to_process_reconcile_time(namespace, resource)
@@ -636,11 +640,9 @@ class OCCli:
636
640
  def project_exists(self, name: str) -> bool:
637
641
  if name in self.projects:
638
642
  return True
643
+ kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
639
644
  try:
640
- if self.is_kind_supported("Project"):
641
- self.get(None, "Project.project.openshift.io", name)
642
- else:
643
- self.get(None, "Namespace", name)
645
+ self.get(None, kind, name)
644
646
  except StatusCodeError as e:
645
647
  if "NotFound" in str(e):
646
648
  return False
@@ -649,7 +651,7 @@ class OCCli:
649
651
 
650
652
  @OCDecorators.process_reconcile_time
651
653
  def new_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
652
- if self.is_kind_supported("Project"):
654
+ if self.is_kind_supported(PROJECT_KIND):
653
655
  cmd = ["new-project", namespace]
654
656
  else:
655
657
  cmd = ["create", "namespace", namespace]
@@ -665,7 +667,7 @@ class OCCli:
665
667
 
666
668
  @OCDecorators.process_reconcile_time
667
669
  def delete_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
668
- if self.is_kind_supported("Project"):
670
+ if self.is_kind_supported(PROJECT_KIND):
669
671
  cmd = ["delete", "project", namespace]
670
672
  else:
671
673
  cmd = ["delete", "namespace", namespace]
@@ -714,9 +716,9 @@ class OCCli:
714
716
 
715
717
  def sa_get_token(self, namespace: str, name: str) -> str:
716
718
  cmd = ["sa", "-n", namespace, "get-token", name]
717
- return self._run(cmd)
719
+ return self._run(cmd).decode("utf-8")
718
720
 
719
- def get_api_resources(self) -> dict[str, Any]:
721
+ def get_api_resources(self) -> dict[str, list[OCCliApiResource]]:
720
722
  with self.api_resources_lock:
721
723
  if not self.api_resources:
722
724
  cmd = ["api-resources", "--no-headers"]
@@ -1009,7 +1011,7 @@ class OCCli:
1009
1011
  name = obj["metadata"]["name"]
1010
1012
  logging.info([f"recycle_{kind.lower()}", self.cluster_name, namespace, name])
1011
1013
  if not dry_run:
1012
- now = datetime.now()
1014
+ now = utc_now()
1013
1015
  recycle_time = now.strftime("%d/%m/%Y %H:%M:%S")
1014
1016
 
1015
1017
  # get the object in case it was modified
@@ -1020,7 +1022,7 @@ class OCCli:
1020
1022
  a["recycle.time"] = recycle_time
1021
1023
  obj["spec"]["template"]["metadata"]["annotations"] = a
1022
1024
  cmd = ["apply", "-n", namespace, "-f", "-"]
1023
- stdin = json.dumps(obj, sort_keys=True)
1025
+ stdin = json_dumps(obj)
1024
1026
  self._run(cmd, stdin=stdin, apply=True)
1025
1027
 
1026
1028
  def get_obj_root_owner(
@@ -1186,7 +1188,7 @@ class OCCli:
1186
1188
  def _run_json(
1187
1189
  self, cmd: list[str], allow_not_found: bool = False
1188
1190
  ) -> dict[str, Any]:
1189
- out = self._run(cmd, allow_not_found=allow_not_found)
1191
+ out = self._run(cmd, allow_not_found=allow_not_found).decode("utf-8")
1190
1192
 
1191
1193
  try:
1192
1194
  out_json = json.loads(out)
@@ -1195,76 +1197,90 @@ class OCCli:
1195
1197
 
1196
1198
  return out_json
1197
1199
 
1198
- def _parse_kind(self, kind_name: str) -> tuple[str, str]:
1199
- # This is a provisional solution while we work in redefining
1200
- # the api resources initialization.
1201
- if not self.api_resources:
1202
- self.get_api_resources()
1200
+ def parse_kind(self, kind: str) -> tuple[str, str, str]:
1201
+ """Parse a Kubernetes kind string into its components.
1203
1202
 
1204
- kind_group = kind_name.split(".", 1)
1205
- kind = kind_group[0]
1206
- if kind in self.api_resources:
1207
- group_version = self.api_resources[kind][0].group_version
1208
- else:
1209
- raise StatusCodeError(f"{self.server}: {kind} does not exist")
1210
-
1211
- # if a kind_group has more than 1 entry than the kind_name is in
1212
- # the format kind.apigroup. Find the apigroup/version that matches
1213
- # the apigroup passed with the kind_name
1214
- if len(kind_group) > 1:
1215
- apigroup_override = kind_group[1]
1216
- find = False
1217
- for gv in self.api_resources[kind]:
1218
- if apigroup_override == gv.group:
1219
- if not gv.group:
1220
- group_version = gv.api_version
1221
- else:
1222
- group_version = f"{gv.group}/{gv.api_version}"
1223
- find = True
1224
- break
1203
+ Supports three formats:
1204
+ - kind
1205
+ - kind.group.whatever
1206
+ - kind.group.whatever/version
1225
1207
 
1226
- if not find:
1227
- raise StatusCodeError(
1228
- f"{self.server}: {apigroup_override} does not have kind {kind}"
1229
- )
1230
- return (kind, group_version)
1208
+ Args:
1209
+ kind: A Kubernetes kind string in one of the supported formats
1210
+
1211
+ Returns:
1212
+ Tuple of (kind, group, version) where missing parts are empty strings
1213
+
1214
+ Raises:
1215
+ ValueError: If the kind string format is invalid
1216
+
1217
+ Examples:
1218
+ >>> parse_kind_string("Deployment")
1219
+ ('Deployment', '', '')
1220
+ >>> parse_kind_string("ClusterRoleBinding.rbac.authorization.k8s.io")
1221
+ ('ClusterRoleBinding', 'rbac.authorization.k8s.io', '')
1222
+ >>> parse_kind_string("CustomResource.mygroup.example.com/v1")
1223
+ ('CustomResource', 'mygroup.example.com', 'v1')
1224
+ """
1225
+ pattern = r"^(?P<kind>[^./]+)(?:\.(?P<group>[^/]+))?(?:/(?P<version>.+))?$"
1226
+ match = re.match(pattern, kind)
1227
+ if not match:
1228
+ raise ValueError(f"Invalid kind string: {kind}")
1229
+
1230
+ kind = match.group("kind") or ""
1231
+ group = match.group("group") or DEFAULT_GROUP
1232
+ version = match.group("version") or ""
1233
+
1234
+ return kind, group, version
1231
1235
 
1232
1236
  def is_kind_supported(self, kind: str) -> bool:
1233
- # This is a provisional solution while we work in redefining
1234
- # the api resources initialization.
1235
- if not self.api_resources:
1236
- self.get_api_resources()
1237
+ """Returns True if the given kind is supported by the cluster, False otherwise.
1237
1238
 
1238
- if "." in kind:
1239
- try:
1240
- self._parse_kind(kind)
1241
- return True
1242
- except StatusCodeError:
1243
- return False
1244
- else:
1245
- return kind in self.api_resources
1239
+ Kind can be either kind, kind.group or kind.group/version."""
1240
+ try:
1241
+ self.get_api_resource(kind)
1242
+ return True
1243
+ except KindNotFoundError:
1244
+ return False
1246
1245
 
1247
1246
  def is_kind_namespaced(self, kind: str) -> bool:
1248
- # This is a provisional solution while we work in redefining
1249
- # the api resources initialization.
1247
+ """Returns True if the given kind is namespaced, False if it's cluster scoped.
1248
+
1249
+ Kind can be either kind, kind.group or kind.group/version."""
1250
+ return self.get_api_resource(kind).namespaced
1251
+
1252
+ def get_api_resource(self, kind: str) -> OCCliApiResource:
1253
+ """Return the OCCliApiResource for the given resource type.
1254
+
1255
+ Resource type can be either kind, kind.group or kind.group/version.
1256
+ If kind is not unique, group must be specified."""
1257
+
1250
1258
  if not self.api_resources:
1251
- self.get_api_resources()
1259
+ raise RuntimeError("API resources not initialized")
1260
+
1261
+ kind, group, _ = self.parse_kind(kind)
1252
1262
 
1253
- kg = kind.split(".", 1)
1254
- kind = kg[0]
1263
+ if not (resources := self.api_resources.get(kind)):
1264
+ # the kind not found at all
1265
+ raise KindNotFoundError(f"Unsupported resource type: {kind}")
1255
1266
 
1256
- # Same Kinds might exist in different api groups
1257
- kind_resources = self.api_resources.get(kind)
1258
- if not kind_resources:
1259
- raise StatusCodeError(f"Kind {kind} does not exist in the ApiServer")
1267
+ if len(resources) == 1 and group == DEFAULT_GROUP:
1268
+ return resources[0]
1260
1269
 
1261
- if len(kg) > 1:
1262
- group = kg[1]
1263
- for r in kind_resources:
1264
- if group == r.group:
1265
- return r.namespaced
1266
- raise StatusCodeError(f"Kind: {kind} does nod exist in the ApiServer")
1267
- return kind_resources[0].namespaced
1270
+ # get the resource with the specified group
1271
+ if resource := next((r for r in resources if r.group == group), None):
1272
+ return resource
1273
+
1274
+ # no resource with the specified group found
1275
+ if group == DEFAULT_GROUP:
1276
+ message = (
1277
+ f"Ambiguous resource type: {kind}. "
1278
+ "Please fully qualify it with its API group. E.g., ClusterRoleBinding -> ClusterRoleBinding.rbac.authorization.k8s.io"
1279
+ )
1280
+ raise AmbiguousResourceTypeError(message)
1281
+
1282
+ # group was specified but no matching resource found
1283
+ raise KindNotFoundError(f"Unsupported resource type: {kind}")
1268
1284
 
1269
1285
 
1270
1286
  REQUEST_TIMEOUT = 60
@@ -1304,20 +1320,19 @@ class OCNative(OCCli):
1304
1320
 
1305
1321
  server = connection_parameters.server_url
1306
1322
 
1307
- if server:
1308
- self.client = self._get_client(server, token)
1309
- self.api_resources = self.get_api_resources()
1323
+ if not server:
1324
+ raise Exception("Server name is required!")
1310
1325
 
1311
- else:
1312
- raise Exception("A method relies on client/api_kind_version to be set")
1326
+ if not token:
1327
+ raise Exception("Token is required!")
1328
+
1329
+ self.client = self._get_client(server, token)
1330
+ self.api_resources = self.get_api_resources()
1313
1331
 
1314
1332
  self.projects = set()
1315
1333
  self.init_projects = init_projects
1316
1334
  if self.init_projects:
1317
- if self.is_kind_supported("Project"):
1318
- kind = "Project.project.openshift.io"
1319
- else:
1320
- kind = "Namespace"
1335
+ kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
1321
1336
  self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
1322
1337
 
1323
1338
  def __enter__(self) -> OCNative:
@@ -1367,8 +1382,10 @@ class OCNative(OCCli):
1367
1382
 
1368
1383
  @retry(max_attempts=5, exceptions=(ServerTimeoutError))
1369
1384
  def get_items(self, kind: str, **kwargs: Any) -> list[dict[str, Any]]:
1370
- k, group_version = self._parse_kind(kind)
1371
- obj_client = self._get_obj_client(group_version=group_version, kind=k)
1385
+ resource = self.get_api_resource(kind)
1386
+ obj_client = self._get_obj_client(
1387
+ group_version=resource.group_version, kind=resource.kind
1388
+ )
1372
1389
 
1373
1390
  namespace = ""
1374
1391
  if "namespace" in kwargs:
@@ -1420,8 +1437,10 @@ class OCNative(OCCli):
1420
1437
  name: str | None = None,
1421
1438
  allow_not_found: bool = False,
1422
1439
  ) -> dict[str, Any]:
1423
- k, group_version = self._parse_kind(kind)
1424
- obj_client = self._get_obj_client(group_version=group_version, kind=k)
1440
+ resource = self.get_api_resource(kind)
1441
+ obj_client = self._get_obj_client(
1442
+ group_version=resource.group_version, kind=resource.kind
1443
+ )
1425
1444
  try:
1426
1445
  obj = obj_client.get(
1427
1446
  name=name,
@@ -1435,8 +1454,10 @@ class OCNative(OCCli):
1435
1454
  raise StatusCodeError(f"[{self.server}]: {e}") from None
1436
1455
 
1437
1456
  def get_all(self, kind: str, all_namespaces: bool = False) -> dict[str, Any]:
1438
- k, group_version = self._parse_kind(kind)
1439
- obj_client = self._get_obj_client(group_version=group_version, kind=k)
1457
+ resource = self.get_api_resource(kind)
1458
+ obj_client = self._get_obj_client(
1459
+ group_version=resource.group_version, kind=resource.kind
1460
+ )
1440
1461
  try:
1441
1462
  return obj_client.get(_request_timeout=REQUEST_TIMEOUT).to_dict()
1442
1463
  except NotFoundError as e:
@@ -19,19 +19,19 @@ class Namespace(Protocol):
19
19
  NS = TypeVar("NS", bound=Namespace)
20
20
 
21
21
 
22
- def filter_namespaces_by_cluster(
22
+ def filter_namespaces_by_cluster[NS: Namespace](
23
23
  namespaces: Iterable[NS], cluster_names: Iterable[str]
24
24
  ) -> list[NS]:
25
25
  return [n for n in namespaces if n.cluster.name in cluster_names]
26
26
 
27
27
 
28
- def filter_namespaces_by_name(
28
+ def filter_namespaces_by_name[NS: Namespace](
29
29
  namespaces: Iterable[NS], namespace_names: Iterable[str]
30
30
  ) -> list[NS]:
31
31
  return [n for n in namespaces if n.name in namespace_names]
32
32
 
33
33
 
34
- def filter_namespaces_by_cluster_and_namespace(
34
+ def filter_namespaces_by_cluster_and_namespace[NS: Namespace](
35
35
  namespaces: Iterable[NS],
36
36
  cluster_names: Iterable[str] | None,
37
37
  namespace_names: Iterable[str] | None,
@@ -188,7 +188,6 @@ class AddonServiceV2(AddonService):
188
188
  next_run=policy.get("next_run"),
189
189
  version=policy["version"],
190
190
  state=policy.get("state"),
191
- addon_service=self,
192
191
  )
193
192
  )
194
193
 
@@ -141,16 +141,16 @@ class OCMClusterAWSOperatorRole(BaseModel):
141
141
 
142
142
 
143
143
  class OCMAWSSTS(OCMClusterFlag):
144
- role_arn: str | None
145
- support_role_arn: str | None
146
- oidc_endpoint_url: str | None
147
- operator_iam_roles: list[OCMClusterAWSOperatorRole] | None
148
- instance_iam_roles: dict[str, str] | None
149
- operator_role_prefix: str | None
144
+ role_arn: str | None = None
145
+ support_role_arn: str | None = None
146
+ oidc_endpoint_url: str | None = None
147
+ operator_iam_roles: list[OCMClusterAWSOperatorRole] | None = None
148
+ instance_iam_roles: dict[str, str] | None = None
149
+ operator_role_prefix: str | None = None
150
150
 
151
151
 
152
152
  class OCMClusterAWSSettings(BaseModel):
153
- sts: OCMAWSSTS | None
153
+ sts: OCMAWSSTS | None = None
154
154
 
155
155
  @property
156
156
  def sts_enabled(self) -> bool:
@@ -264,21 +264,21 @@ class OCMCluster(BaseModel):
264
264
  product: OCMModelLink
265
265
  identity_providers: OCMCollectionLink
266
266
 
267
- aws: OCMClusterAWSSettings | None
267
+ aws: OCMClusterAWSSettings | None = None
268
268
 
269
269
  version: OCMClusterVersion
270
270
 
271
271
  hypershift: OCMClusterFlag
272
272
 
273
- console: OCMClusterConsole | None
273
+ console: OCMClusterConsole | None = None
274
274
 
275
- api: OCMClusterAPI | None
275
+ api: OCMClusterAPI | None = None
276
276
 
277
- dns: OCMClusterDns | None
277
+ dns: OCMClusterDns | None = None
278
278
 
279
- external_configuration: OCMExternalConfiguration | None
279
+ external_configuration: OCMExternalConfiguration | None = None
280
280
 
281
- external_auth_config: OCMExternalAuthConfig | None
281
+ external_auth_config: OCMExternalAuthConfig | None = None
282
282
 
283
283
  def minor_version(self) -> str:
284
284
  version_info = parse_semver(self.version.raw_id)
@@ -570,15 +570,12 @@ class OCMOIdentityProviderGithub(OCMOIdentityProvider):
570
570
  )
571
571
 
572
572
 
573
- class OCMOIdentityProviderOidcOpenIdClaims(BaseModel):
573
+ class OCMOIdentityProviderOidcOpenIdClaims(BaseModel, frozen=True):
574
574
  email: list[str]
575
575
  name: list[str] = []
576
576
  preferred_username: list[str]
577
577
  groups: list[str] = []
578
578
 
579
- class Config:
580
- frozen = True
581
-
582
579
 
583
580
  class OCMOIdentityProviderOidcOpenId(BaseModel):
584
581
  client_id: str
@@ -618,11 +615,11 @@ class OCMAddonUpgradePolicy(BaseModel):
618
615
  id: str
619
616
  addon_id: str
620
617
  cluster_id: str
621
- next_run: str | None
622
- schedule: str | None
618
+ next_run: str | None = None
619
+ schedule: str | None = None
623
620
  schedule_type: str
624
621
  version: str
625
- state: str | None
622
+ state: str | None = None
626
623
 
627
624
 
628
625
  def build_label_container(
@@ -17,7 +17,7 @@ def add_user_to_cluster_group(
17
17
  """
18
18
  ocm_api.post(
19
19
  build_cluster_group_users_url(cluster_id, group),
20
- OCMClusterUser(id=user_name).dict(by_alias=True),
20
+ OCMClusterUser(id=user_name).model_dump(by_alias=True),
21
21
  )
22
22
 
23
23
 
@@ -42,7 +42,7 @@ def add_identity_provider(
42
42
  )
43
43
  ocm_api.post(
44
44
  api_path=ocm_cluster.identity_providers.href,
45
- data=idp.dict(by_alias=True, exclude_none=True),
45
+ data=idp.model_dump(by_alias=True, exclude_none=True),
46
46
  )
47
47
 
48
48
 
@@ -55,7 +55,7 @@ def update_identity_provider(
55
55
  raise ValueError(f"IDP {idp.name} does not have a href!")
56
56
  ocm_api.patch(
57
57
  api_path=idp.href,
58
- data=idp.dict(by_alias=True, exclude_none=True, exclude={"name"}),
58
+ data=idp.model_dump(by_alias=True, exclude_none=True, exclude={"name"}),
59
59
  )
60
60
 
61
61
 
@@ -159,7 +159,7 @@ def build_container_for_prefix(
159
159
 
160
160
  return LabelContainer(
161
161
  labels={
162
- strip_prefix_if_needed(label.key): label.copy(
162
+ strip_prefix_if_needed(label.key): label.model_copy(
163
163
  update={"key": strip_prefix_if_needed(label.key)}
164
164
  )
165
165
  for label in container.labels.values()
@@ -47,6 +47,7 @@ SPEC_ATTR_MULTI_AZ = "multi_az"
47
47
  SPEC_ATTR_HYPERSHIFT = "hypershift"
48
48
  SPEC_ATTR_SUBNET_IDS = "subnet_ids"
49
49
  SPEC_ATTR_AVAILABILITY_ZONES = "availability_zones"
50
+ SPEC_ATTR_FIPS = "fips"
50
51
 
51
52
  SPEC_ATTR_NETWORK = "network"
52
53
  IGNORE_NETWORK_TYPE_ATTR = "type"
@@ -177,10 +178,11 @@ class OCMProductOsd(OCMProduct):
177
178
  ],
178
179
  provision_shard_id=provision_shard_id,
179
180
  hypershift=cluster["hypershift"]["enabled"],
181
+ fips=cluster.get("fips") or False,
180
182
  )
181
183
 
182
184
  if not cluster["ccs"]["enabled"]:
183
- cluster_spec_data = spec.dict()
185
+ cluster_spec_data = spec.model_dump()
184
186
  cluster_spec_data["storage"] = (
185
187
  cluster["storage_quota"]["value"] // BYTES_IN_GIGABYTE
186
188
  )
@@ -227,7 +229,7 @@ class OCMProductOsd(OCMProduct):
227
229
  "compute_machine_type": {"id": default_machine_pool.instance_type},
228
230
  }
229
231
  if default_machine_pool.autoscale is not None:
230
- spec["autoscale_compute"] = default_machine_pool.autoscale.dict()
232
+ spec["autoscale_compute"] = default_machine_pool.autoscale.model_dump()
231
233
  else:
232
234
  spec["compute"] = default_machine_pool.replicas
233
235
  return spec
@@ -257,6 +259,7 @@ class OCMProductOsd(OCMProduct):
257
259
  if (duwm := cluster.spec.disable_user_workload_monitoring) is not None
258
260
  else True
259
261
  ),
262
+ "fips": cluster.spec.fips,
260
263
  }
261
264
 
262
265
  # Workaround to enable type checks.
@@ -426,6 +429,7 @@ class OCMProductRosa(OCMProduct):
426
429
  subnet_ids=cluster["aws"].get("subnet_ids"),
427
430
  availability_zones=cluster["nodes"].get("availability_zones"),
428
431
  oidc_endpoint_url=oidc_endpoint_url,
432
+ fips=cluster.get("fips") or False,
429
433
  )
430
434
 
431
435
  machine_pools = [
@@ -470,7 +474,7 @@ class OCMProductRosa(OCMProduct):
470
474
  "compute_machine_type": {"id": default_machine_pool.instance_type},
471
475
  }
472
476
  if default_machine_pool.autoscale is not None:
473
- spec["autoscale_compute"] = default_machine_pool.autoscale.dict()
477
+ spec["autoscale_compute"] = default_machine_pool.autoscale.model_dump()
474
478
  else:
475
479
  spec["compute"] = default_machine_pool.replicas
476
480
  return spec
@@ -513,6 +517,7 @@ class OCMProductRosa(OCMProduct):
513
517
  if (duwm := cluster.spec.disable_user_workload_monitoring) is not None
514
518
  else True
515
519
  ),
520
+ "fips": cluster.spec.fips,
516
521
  }
517
522
 
518
523
  provision_shard_id = cluster.spec.provision_shard_id
@@ -701,6 +706,7 @@ class OCMProductHypershift(OCMProduct):
701
706
  availability_zones=cluster["nodes"].get("availability_zones"),
702
707
  hypershift=cluster["hypershift"]["enabled"],
703
708
  oidc_endpoint_url=oidc_endpoint_url,
709
+ fips=cluster.get("fips") or False,
704
710
  )
705
711
 
706
712
  network = OCMClusterNetwork(
@@ -5,7 +5,6 @@ from abc import (
5
5
  from collections.abc import Iterable
6
6
  from dataclasses import dataclass
7
7
  from datetime import (
8
- UTC,
9
8
  datetime,
10
9
  )
11
10
  from enum import Enum
@@ -16,6 +15,8 @@ from typing import (
16
15
 
17
16
  import dateparser
18
17
 
18
+ from reconcile.utils.datetime_util import utc_now
19
+
19
20
 
20
21
  @dataclass
21
22
  class FilterCondition:
@@ -166,17 +167,13 @@ class DateRangeCondition(FilterCondition):
166
167
  return date
167
168
  parsed = dateparser.parse(
168
169
  date,
169
- settings={"RELATIVE_BASE": DateRangeCondition.now()},
170
+ settings={"RELATIVE_BASE": utc_now()},
170
171
  )
171
172
  if parsed is None:
172
173
  raise InvalidFilterError(f"Invalid relative date: {date}")
173
174
 
174
175
  return parsed
175
176
 
176
- @staticmethod
177
- def now() -> datetime:
178
- return datetime.now(tz=UTC)
179
-
180
177
 
181
178
  class InvalidFilterError(Exception):
182
179
  pass
@@ -1,9 +1,7 @@
1
1
  from collections.abc import Generator
2
- from datetime import (
3
- datetime,
4
- timedelta,
5
- )
2
+ from datetime import timedelta
6
3
 
4
+ from reconcile.utils.datetime_util import utc_now
7
5
  from reconcile.utils.ocm.base import (
8
6
  OCMClusterServiceLog,
9
7
  OCMClusterServiceLogCreateModel,
@@ -47,7 +45,7 @@ def create_service_log(
47
45
  .eq("severity", service_log.severity.value)
48
46
  .eq("summary", service_log.summary)
49
47
  .eq("description", service_log.description)
50
- .after("created_at", datetime.utcnow() - dedup_interval),
48
+ .after("created_at", utc_now() - dedup_interval),
51
49
  ),
52
50
  None,
53
51
  )
@@ -57,6 +55,6 @@ def create_service_log(
57
55
  return OCMClusterServiceLog(
58
56
  **ocm_api.post(
59
57
  api_path=CLUSTER_SERVICE_LOGS_CREATE_ENDPOINT,
60
- data=service_log.dict(by_alias=True),
58
+ data=service_log.model_dump(by_alias=True),
61
59
  )
62
60
  )