qontract-reconcile 0.10.2.dev334__py3-none-any.whl → 0.10.2.dev439__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qontract-reconcile might be problematic. Click here for more details.

Files changed (370) hide show
  1. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +366 -361
  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 +3 -3
  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 +6 -12
  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 +11 -3
  28. reconcile/cli.py +111 -18
  29. reconcile/dashdotdb_dora.py +5 -12
  30. reconcile/dashdotdb_slo.py +1 -1
  31. reconcile/database_access_manager.py +123 -117
  32. reconcile/dynatrace_token_provider/integration.py +1 -1
  33. reconcile/endpoints_discovery/integration.py +4 -1
  34. reconcile/endpoints_discovery/merge_request.py +1 -1
  35. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  36. reconcile/external_resources/factories.py +5 -12
  37. reconcile/external_resources/integration.py +1 -1
  38. reconcile/external_resources/manager.py +8 -5
  39. reconcile/external_resources/meta.py +0 -1
  40. reconcile/external_resources/metrics.py +1 -1
  41. reconcile/external_resources/model.py +20 -20
  42. reconcile/external_resources/reconciler.py +7 -4
  43. reconcile/external_resources/secrets_sync.py +8 -11
  44. reconcile/external_resources/state.py +26 -16
  45. reconcile/fleet_labeler/integration.py +1 -1
  46. reconcile/gabi_authorized_users.py +8 -5
  47. reconcile/gcp_image_mirror.py +2 -2
  48. reconcile/github_org.py +1 -1
  49. reconcile/github_owners.py +4 -0
  50. reconcile/gitlab_housekeeping.py +13 -15
  51. reconcile/gitlab_members.py +6 -12
  52. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  53. reconcile/gitlab_owners.py +15 -11
  54. reconcile/gitlab_permissions.py +8 -12
  55. reconcile/glitchtip_project_alerts/integration.py +3 -1
  56. reconcile/gql_definitions/acs/acs_instances.py +10 -10
  57. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  58. reconcile/gql_definitions/acs/acs_rbac.py +6 -6
  59. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
  60. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
  61. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
  62. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  63. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  64. reconcile/gql_definitions/automated_actions/instance.py +51 -12
  65. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
  66. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
  67. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
  68. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
  69. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
  70. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  71. reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
  72. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  73. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  74. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
  75. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
  76. reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
  77. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  78. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  79. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  80. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  81. reconcile/gql_definitions/common/app_interface_roles.py +120 -0
  82. reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
  83. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  84. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  85. reconcile/gql_definitions/common/apps.py +5 -5
  86. reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
  87. reconcile/gql_definitions/common/aws_vpcs.py +11 -11
  88. reconcile/gql_definitions/common/clusters.py +37 -35
  89. reconcile/gql_definitions/common/clusters_minimal.py +14 -14
  90. reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
  91. reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
  92. reconcile/gql_definitions/common/github_orgs.py +10 -10
  93. reconcile/gql_definitions/common/jira_settings.py +10 -10
  94. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  95. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  96. reconcile/gql_definitions/common/namespaces.py +42 -44
  97. reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
  98. reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
  99. reconcile/gql_definitions/common/ocm_environments.py +19 -19
  100. reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
  101. reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
  102. reconcile/gql_definitions/common/pipeline_providers.py +29 -29
  103. reconcile/gql_definitions/common/quay_instances.py +5 -5
  104. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  105. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  106. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  107. reconcile/gql_definitions/common/saas_files.py +44 -44
  108. reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
  109. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  110. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  111. reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
  112. reconcile/gql_definitions/common/state_aws_account.py +7 -8
  113. reconcile/gql_definitions/common/users.py +5 -5
  114. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  115. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  116. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  117. reconcile/gql_definitions/cost_report/settings.py +9 -9
  118. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
  119. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
  120. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  121. reconcile/gql_definitions/email_sender/apps.py +5 -5
  122. reconcile/gql_definitions/email_sender/emails.py +8 -8
  123. reconcile/gql_definitions/email_sender/users.py +6 -6
  124. reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
  125. reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
  126. reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
  127. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
  128. reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
  129. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  130. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
  131. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  132. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  133. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  135. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  136. reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
  137. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  138. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  139. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  140. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  141. reconcile/gql_definitions/fragments/disable.py +5 -5
  142. reconcile/gql_definitions/fragments/email_service.py +5 -5
  143. reconcile/gql_definitions/fragments/email_user.py +5 -5
  144. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  145. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  146. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  147. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  148. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  149. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  150. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  151. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  152. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  153. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  154. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  155. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  156. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  157. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  158. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  159. reconcile/gql_definitions/fragments/user.py +5 -5
  160. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  161. reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
  162. reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
  163. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
  164. reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
  165. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
  166. reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
  167. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
  168. reconcile/gql_definitions/integrations/integrations.py +48 -51
  169. reconcile/gql_definitions/introspection.json +3207 -1683
  170. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
  171. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
  172. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  173. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
  174. reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
  175. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  176. reconcile/gql_definitions/ldap_groups/settings.py +9 -9
  177. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  178. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  179. reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
  180. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  181. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
  182. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  183. reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
  184. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
  185. reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
  186. reconcile/gql_definitions/rhcs/certs.py +33 -87
  187. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  188. reconcile/gql_definitions/rhidp/organizations.py +18 -18
  189. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  190. reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
  191. reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
  192. reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
  193. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  194. reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
  195. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  196. reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
  197. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  198. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  199. reconcile/gql_definitions/status_board/status_board.py +6 -7
  200. reconcile/gql_definitions/statuspage/statuspages.py +9 -9
  201. reconcile/gql_definitions/templating/template_collection.py +5 -5
  202. reconcile/gql_definitions/templating/templates.py +5 -5
  203. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
  204. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
  205. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
  206. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
  207. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
  208. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
  209. reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
  210. reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
  211. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  212. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +440 -407
  213. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
  214. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
  215. reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
  216. reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
  217. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
  218. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  219. reconcile/integrations_manager.py +3 -3
  220. reconcile/jenkins_worker_fleets.py +10 -8
  221. reconcile/jira_permissions_validator.py +237 -122
  222. reconcile/ldap_groups/integration.py +1 -1
  223. reconcile/ocm/types.py +35 -56
  224. reconcile/ocm_aws_infrastructure_access.py +1 -1
  225. reconcile/ocm_clusters.py +4 -4
  226. reconcile/ocm_labels/integration.py +3 -2
  227. reconcile/ocm_machine_pools.py +33 -27
  228. reconcile/openshift_base.py +113 -5
  229. reconcile/openshift_cluster_bots.py +3 -2
  230. reconcile/openshift_namespace_labels.py +1 -1
  231. reconcile/openshift_namespaces.py +97 -101
  232. reconcile/openshift_resources_base.py +6 -2
  233. reconcile/openshift_rhcs_certs.py +74 -37
  234. reconcile/openshift_rolebindings.py +230 -130
  235. reconcile/openshift_saas_deploy.py +6 -7
  236. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  237. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  238. reconcile/openshift_serviceaccount_tokens.py +2 -2
  239. reconcile/openshift_upgrade_watcher.py +4 -4
  240. reconcile/openshift_users.py +5 -3
  241. reconcile/oum/labelset.py +5 -3
  242. reconcile/oum/models.py +1 -4
  243. reconcile/prometheus_rules_tester/integration.py +3 -3
  244. reconcile/quay_mirror.py +1 -1
  245. reconcile/queries.py +131 -0
  246. reconcile/rhidp/common.py +3 -5
  247. reconcile/rhidp/sso_client/base.py +16 -5
  248. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  249. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  250. reconcile/skupper_network/integration.py +2 -2
  251. reconcile/slack_usergroups.py +35 -14
  252. reconcile/sql_query.py +1 -0
  253. reconcile/status_board.py +6 -6
  254. reconcile/statuspage/atlassian.py +7 -7
  255. reconcile/statuspage/integrations/maintenances.py +4 -3
  256. reconcile/statuspage/page.py +4 -9
  257. reconcile/statuspage/status.py +5 -8
  258. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +5 -1
  259. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
  260. reconcile/templating/lib/merge_request_manager.py +2 -2
  261. reconcile/templating/lib/rendering.py +3 -3
  262. reconcile/templating/renderer.py +12 -13
  263. reconcile/terraform_aws_route53.py +7 -1
  264. reconcile/terraform_cloudflare_dns.py +3 -3
  265. reconcile/terraform_cloudflare_resources.py +5 -5
  266. reconcile/terraform_cloudflare_users.py +3 -2
  267. reconcile/terraform_init/integration.py +187 -23
  268. reconcile/terraform_repo.py +16 -12
  269. reconcile/terraform_resources.py +17 -7
  270. reconcile/terraform_tgw_attachments.py +27 -19
  271. reconcile/terraform_users.py +7 -0
  272. reconcile/terraform_vpc_peerings.py +14 -3
  273. reconcile/terraform_vpc_resources/integration.py +20 -8
  274. reconcile/typed_queries/app_interface_roles.py +10 -0
  275. reconcile/typed_queries/aws_account_tags.py +41 -0
  276. reconcile/typed_queries/cost_report/app_names.py +1 -1
  277. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  278. reconcile/typed_queries/saas_files.py +13 -13
  279. reconcile/typed_queries/status_board.py +2 -2
  280. reconcile/unleash_feature_toggles/integration.py +4 -2
  281. reconcile/utils/acs/base.py +6 -3
  282. reconcile/utils/acs/policies.py +2 -2
  283. reconcile/utils/aggregated_list.py +4 -3
  284. reconcile/utils/aws_api.py +51 -20
  285. reconcile/utils/aws_api_typed/api.py +38 -9
  286. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  287. reconcile/utils/aws_api_typed/logs.py +73 -0
  288. reconcile/utils/aws_api_typed/organization.py +4 -2
  289. reconcile/utils/binary.py +7 -12
  290. reconcile/utils/datetime_util.py +67 -0
  291. reconcile/utils/deadmanssnitch_api.py +1 -1
  292. reconcile/utils/differ.py +2 -3
  293. reconcile/utils/early_exit_cache.py +11 -12
  294. reconcile/utils/expiration.py +7 -3
  295. reconcile/utils/external_resource_spec.py +24 -1
  296. reconcile/utils/filtering.py +1 -1
  297. reconcile/utils/gitlab_api.py +7 -5
  298. reconcile/utils/glitchtip/client.py +6 -2
  299. reconcile/utils/glitchtip/models.py +25 -28
  300. reconcile/utils/gql.py +4 -7
  301. reconcile/utils/helm.py +2 -1
  302. reconcile/utils/helpers.py +1 -1
  303. reconcile/utils/instrumented_wrappers.py +1 -1
  304. reconcile/utils/internal_groups/client.py +2 -2
  305. reconcile/utils/internal_groups/models.py +8 -17
  306. reconcile/utils/jinja2/utils.py +6 -8
  307. reconcile/utils/jira_client.py +82 -63
  308. reconcile/utils/jjb_client.py +28 -15
  309. reconcile/utils/jobcontroller/controller.py +2 -2
  310. reconcile/utils/jobcontroller/models.py +17 -1
  311. reconcile/utils/json.py +74 -0
  312. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  313. reconcile/utils/membershipsources/models.py +16 -23
  314. reconcile/utils/membershipsources/resolver.py +4 -2
  315. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  316. reconcile/utils/merge_request_manager/parser.py +6 -6
  317. reconcile/utils/metrics.py +5 -5
  318. reconcile/utils/models.py +304 -82
  319. reconcile/utils/mr/app_interface_reporter.py +2 -2
  320. reconcile/utils/mr/base.py +2 -2
  321. reconcile/utils/mr/notificator.py +3 -3
  322. reconcile/utils/mr/update_access_report_base.py +3 -4
  323. reconcile/utils/mr/user_maintenance.py +3 -2
  324. reconcile/utils/oc.py +249 -203
  325. reconcile/utils/oc_filters.py +3 -3
  326. reconcile/utils/ocm/addons.py +0 -1
  327. reconcile/utils/ocm/base.py +18 -21
  328. reconcile/utils/ocm/cluster_groups.py +1 -1
  329. reconcile/utils/ocm/identity_providers.py +2 -2
  330. reconcile/utils/ocm/labels.py +1 -1
  331. reconcile/utils/ocm/products.py +9 -3
  332. reconcile/utils/ocm/search_filters.py +3 -6
  333. reconcile/utils/ocm/service_log.py +4 -6
  334. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  335. reconcile/utils/openshift_resource.py +10 -5
  336. reconcile/utils/output.py +3 -2
  337. reconcile/utils/pagerduty_api.py +10 -7
  338. reconcile/utils/promotion_state.py +6 -11
  339. reconcile/utils/raw_github_api.py +1 -1
  340. reconcile/utils/rhcsv2_certs.py +138 -35
  341. reconcile/utils/rosa/session.py +16 -0
  342. reconcile/utils/runtime/integration.py +2 -3
  343. reconcile/utils/runtime/runner.py +2 -2
  344. reconcile/utils/saasherder/interfaces.py +13 -20
  345. reconcile/utils/saasherder/models.py +25 -21
  346. reconcile/utils/saasherder/saasherder.py +55 -31
  347. reconcile/utils/slack_api.py +26 -4
  348. reconcile/utils/sloth.py +224 -0
  349. reconcile/utils/sqs_gateway.py +2 -1
  350. reconcile/utils/state.py +2 -1
  351. reconcile/utils/structs.py +1 -1
  352. reconcile/utils/terraform_client.py +5 -4
  353. reconcile/utils/terrascript_aws_client.py +192 -114
  354. reconcile/utils/unleash/server.py +2 -8
  355. reconcile/utils/vault.py +5 -12
  356. reconcile/utils/vcs.py +8 -8
  357. reconcile/vault_replication.py +107 -42
  358. tools/app_interface_reporter.py +4 -4
  359. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  360. tools/cli_commands/cost_report/view.py +7 -6
  361. tools/cli_commands/erv2.py +1 -1
  362. tools/cli_commands/systems_and_tools.py +5 -1
  363. tools/qontract_cli.py +31 -18
  364. tools/template_validation.py +3 -1
  365. reconcile/gql_definitions/cna/__init__.py +0 -0
  366. reconcile/gql_definitions/cna/queries/__init__.py +0 -0
  367. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  368. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  369. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
  370. {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
@@ -24,30 +24,24 @@ class Environment(BaseModel):
24
24
  return self.name == other
25
25
 
26
26
 
27
- class FeatureToggle(BaseModel):
27
+ class FeatureToggle(BaseModel, validate_by_name=True, validate_by_alias=True):
28
28
  name: str
29
29
  type: FeatureToggleType = FeatureToggleType.release
30
30
  description: str | None = None
31
31
  impression_data: bool = Field(False, alias="impressionData")
32
32
  environments: list[Environment]
33
33
 
34
- class Config:
35
- allow_population_by_field_name = True
36
-
37
34
  def __eq__(self, other: object) -> bool:
38
35
  if isinstance(other, FeatureToggle):
39
36
  return self.name == other.name
40
37
  return self.name == other
41
38
 
42
39
 
43
- class Project(BaseModel):
40
+ class Project(BaseModel, validate_by_name=True, validate_by_alias=True):
44
41
  pk: str = Field(alias="id")
45
42
  name: str
46
43
  feature_toggles: list[FeatureToggle] = []
47
44
 
48
- class Config:
49
- allow_population_by_field_name = True
50
-
51
45
 
52
46
  class TokenAuth(BearerTokenAuth):
53
47
  def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
reconcile/utils/vault.py CHANGED
@@ -6,7 +6,7 @@ import threading
6
6
  import time
7
7
  from collections.abc import Mapping
8
8
  from functools import lru_cache
9
- from typing import Any, Self, TypedDict
9
+ from typing import Any, Self
10
10
 
11
11
  import hvac
12
12
  import requests
@@ -48,13 +48,6 @@ class VaultConnectionError(Exception):
48
48
  pass
49
49
 
50
50
 
51
- class Secret(TypedDict):
52
- path: str
53
- field: str
54
- format: str | None
55
- version: str | None
56
-
57
-
58
51
  SECRET_VERSION_LATEST = "LATEST"
59
52
 
60
53
 
@@ -197,7 +190,7 @@ class VaultClient:
197
190
  self._client.auth_approle(self.role_id, self.secret_id)
198
191
 
199
192
  @retry()
200
- def read_all_with_version(self, secret: Mapping) -> tuple[Mapping, str | None]:
193
+ def read_all_with_version(self, secret: Mapping) -> tuple[dict, int | None]:
201
194
  """Returns a dictionary of keys and values in a Vault secret and the
202
195
  version of the secret, for V1 secrets, version will be None.
203
196
 
@@ -207,7 +200,7 @@ class VaultClient:
207
200
  a v2 KV engine)
208
201
  """
209
202
  secret_path = secret["path"]
210
- secret_version = secret.get("version")
203
+ secret_version = secret.get("version") or SECRET_VERSION_LATEST
211
204
 
212
205
  kv_version = self._get_mount_version_by_secret_path(secret_path)
213
206
 
@@ -250,7 +243,7 @@ class VaultClient:
250
243
 
251
244
  def __read_all_v2(
252
245
  self, path: str, version: str | None
253
- ) -> tuple[dict[str, Any], str | None]:
246
+ ) -> tuple[dict[str, Any], int]:
254
247
  path_split = path.split("/")
255
248
  mount_point = path_split[0]
256
249
  read_path = "/".join(path_split[1:])
@@ -294,7 +287,7 @@ class VaultClient:
294
287
  return secret["data"]
295
288
 
296
289
  @retry()
297
- def read(self, secret: Secret) -> Any:
290
+ def read(self, secret: Mapping[str, Any]) -> Any:
298
291
  """Returns a value of a key in a Vault secret.
299
292
 
300
293
  The input secret is a dictionary which contains the following fields:
reconcile/utils/vcs.py CHANGED
@@ -140,7 +140,7 @@ class VCS:
140
140
  gitlab_instances: Iterable[GitlabInstanceV1],
141
141
  ) -> GitLabApi:
142
142
  return GitLabApi(
143
- next(iter(gitlab_instances)).dict(by_alias=True),
143
+ next(iter(gitlab_instances)).model_dump(by_alias=True),
144
144
  secret_reader=self._secret_reader,
145
145
  )
146
146
 
@@ -150,7 +150,7 @@ class VCS:
150
150
  app_interface_repo_url: str,
151
151
  ) -> GitLabApi:
152
152
  return GitLabApi(
153
- next(iter(gitlab_instances)).dict(by_alias=True),
153
+ next(iter(gitlab_instances)).model_dump(by_alias=True),
154
154
  secret_reader=self._secret_reader,
155
155
  project_url=app_interface_repo_url,
156
156
  )
@@ -221,26 +221,26 @@ class VCS:
221
221
  match repo_info.platform:
222
222
  case "github":
223
223
  github = self._init_github(repo_url=repo_url, auth_code=auth_code)
224
- data = github.compare(commit_from=commit_from, commit_to=commit_to)
225
224
  return [
226
225
  Commit(
227
226
  repo=repo_url,
228
227
  sha=gh_commit.sha,
229
228
  date=gh_commit.commit.committer.date,
230
229
  )
231
- for gh_commit in data
230
+ for gh_commit in github.compare(
231
+ commit_from=commit_from, commit_to=commit_to
232
+ )
232
233
  ]
233
234
  case "gitlab":
234
- data = self._gitlab_instance.repository_compare(
235
- repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
236
- )
237
235
  return [
238
236
  Commit(
239
237
  repo=repo_url,
240
238
  sha=gl_commit["id"],
241
239
  date=datetime.fromisoformat(gl_commit["committed_date"]),
242
240
  )
243
- for gl_commit in data
241
+ for gl_commit in self._gitlab_instance.repository_compare(
242
+ repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
243
+ )
244
244
  ]
245
245
  case _:
246
246
  raise ValueError(f"Unsupported repository URL: {repo_url}")
@@ -84,6 +84,54 @@ def deep_copy_versions(
84
84
  dest_vault.write(secret=write_dict, decode_base64=False, force=True)
85
85
 
86
86
 
87
+ def _handle_missing_destination_secret(
88
+ dry_run: bool,
89
+ source_vault: VaultClient,
90
+ dest_vault: VaultClient,
91
+ source_data: dict,
92
+ source_version: int | None,
93
+ path: str,
94
+ ) -> None:
95
+ """Handles replication when destination secret is missing or has no accessible versions.
96
+
97
+ This covers two scenarios:
98
+ 1. Secret doesn't exist at all in destination vault (SecretNotFoundError)
99
+ 2. Secret exists but all versions are deleted in KV v2 (SecretVersionNotFoundError)
100
+
101
+ For both cases, we replicate from source starting from version 0 (or copy directly for v1).
102
+
103
+ Args:
104
+ dry_run: Whether this is a dry run
105
+ source_vault: Source vault client (needed for v2 deep copy)
106
+ dest_vault: Destination vault client
107
+ source_data: Already retrieved source secret data
108
+ source_version: Source secret version (None for v1 secrets)
109
+ path: Secret path
110
+ """
111
+ if source_version is None:
112
+ # v1 secret - just copy it over using the already-retrieved source data
113
+ logging.info(["replicate_vault_secret", "Copying v1 secret", path])
114
+ if not dry_run:
115
+ write_dict = {"path": path, "data": source_data}
116
+ dest_vault.write(secret=write_dict, decode_base64=False, force=True)
117
+ else:
118
+ # v2 secret - deep copy all versions starting from 0
119
+ # Note: deep_copy_versions will read individual versions from source as needed
120
+ logging.info([
121
+ "replicate_vault_secret",
122
+ "Deep copying v2 secret versions",
123
+ path,
124
+ ])
125
+ deep_copy_versions(
126
+ dry_run=dry_run,
127
+ source_vault=source_vault,
128
+ dest_vault=dest_vault,
129
+ current_dest_version=0,
130
+ current_source_version=source_version,
131
+ path=path,
132
+ )
133
+
134
+
87
135
  def write_dummy_versions(
88
136
  dry_run: bool,
89
137
  dest_vault: VaultClient,
@@ -133,48 +181,65 @@ def copy_vault_secret(
133
181
 
134
182
  try:
135
183
  dest_data, dest_version = dest_vault.read_all_with_version(secret_dict)
136
- if dest_version is None and version is None:
137
- # v1 secrets don't have version
138
- if source_data == dest_data:
139
- # If the secret is the same in both vaults, we don't need
140
- # to copy it again
141
- return
142
-
143
- secret, _ = source_vault.read_all_with_version(secret_dict)
144
- write_dict = {"path": path, "data": secret}
145
- logging.info(["replicate_vault_secret", path])
146
- if not dry_run:
147
- # Using force=True to write the secret to force the vault client even
148
- # if the data is the same as the previous version. This happens in
149
- # some secrets even tho the library does not create it
150
- dest_vault.write(secret=write_dict, decode_base64=False, force=True)
151
- elif dest_version < version:
152
- deep_copy_versions(
153
- dry_run=dry_run,
154
- source_vault=source_vault,
155
- dest_vault=dest_vault,
156
- current_dest_version=dest_version,
157
- current_source_version=version,
158
- path=path,
159
- )
160
- except (SecretVersionNotFoundError, SecretNotFoundError):
161
- logging.info(["replicate_vault_secret", "Secret not found", path])
162
- # Handle v1 secrets where version is None and we don't need to deep sync.
163
- if version is None:
164
- logging.info(["replicate_vault_secret", path])
165
- if not dry_run:
166
- secret, _ = source_vault.read_all_with_version(secret_dict)
167
- write_dict = {"path": path, "data": secret}
168
- dest_vault.write(secret=write_dict, decode_base64=False, force=True)
169
- else:
170
- deep_copy_versions(
171
- dry_run=dry_run,
172
- source_vault=source_vault,
173
- dest_vault=dest_vault,
174
- current_dest_version=0,
175
- current_source_version=version,
176
- path=path,
177
- )
184
+ except SecretVersionNotFoundError:
185
+ # Handle KV v2 case where secret metadata exists but latest version is deleted
186
+ # This occurs when someone manually deletes the latest version but the secret
187
+ # metadata still exists in Vault. This should only happen for v2 secrets.
188
+ logging.info([
189
+ "replicate_vault_secret",
190
+ "KV v2 latest version deleted, replicating all versions",
191
+ path,
192
+ ])
193
+ _handle_missing_destination_secret(
194
+ dry_run=dry_run,
195
+ source_vault=source_vault,
196
+ dest_vault=dest_vault,
197
+ source_data=source_data,
198
+ source_version=version,
199
+ path=path,
200
+ )
201
+ return
202
+ except SecretNotFoundError:
203
+ # Handle case where secret doesn't exist at all in destination vault
204
+ logging.info([
205
+ "replicate_vault_secret",
206
+ "Secret not found in destination",
207
+ path,
208
+ ])
209
+ _handle_missing_destination_secret(
210
+ dry_run=dry_run,
211
+ source_vault=source_vault,
212
+ dest_vault=dest_vault,
213
+ source_data=source_data,
214
+ source_version=version,
215
+ path=path,
216
+ )
217
+ return
218
+
219
+ # If we reach here, we successfully read the destination secret
220
+ if dest_version is None or version is None:
221
+ # v1 secrets don't have version
222
+ if source_data == dest_data:
223
+ # If the secret is the same in both vaults, we don't need
224
+ # to copy it again
225
+ return
226
+
227
+ write_dict = {"path": path, "data": source_data}
228
+ logging.info(["replicate_vault_secret", path])
229
+ if not dry_run:
230
+ # Using force=True to write the secret to force the vault client even
231
+ # if the data is the same as the previous version. This happens in
232
+ # some secrets even tho the library does not create it
233
+ dest_vault.write(secret=write_dict, decode_base64=False, force=True)
234
+ elif dest_version < version:
235
+ deep_copy_versions(
236
+ dry_run=dry_run,
237
+ source_vault=source_vault,
238
+ dest_vault=dest_vault,
239
+ current_dest_version=dest_version,
240
+ current_source_version=version,
241
+ path=path,
242
+ )
178
243
 
179
244
 
180
245
  def check_invalid_paths(
@@ -4,7 +4,6 @@ import os
4
4
  import textwrap
5
5
  from collections.abc import Mapping, MutableMapping
6
6
  from datetime import (
7
- UTC,
8
7
  datetime,
9
8
  )
10
9
 
@@ -29,6 +28,7 @@ from reconcile.cli import (
29
28
  )
30
29
  from reconcile.jenkins_job_builder import init_jjb
31
30
  from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
31
+ from reconcile.utils.datetime_util import ensure_utc, utc_now
32
32
  from reconcile.utils.mr import CreateAppInterfaceReporter
33
33
  from reconcile.utils.runtime.environment import init_env
34
34
  from reconcile.utils.secret_reader import SecretReader
@@ -189,8 +189,8 @@ def get_apps_data(
189
189
  apps = queries.get_apps()
190
190
  jjb = init_jjb(secret_reader)
191
191
  jenkins_map = jenkins_base.get_jenkins_map()
192
- time_limit = date - relativedelta(months=month_delta)
193
- timestamp_limit = int(time_limit.replace(tzinfo=UTC).timestamp())
192
+ time_limit = ensure_utc(date) - relativedelta(months=month_delta)
193
+ timestamp_limit = int(time_limit.timestamp())
194
194
 
195
195
  secret_content = secret_reader.read_all({"path": DASHDOTDB_SECRET})
196
196
  dashdotdb_url = secret_content["url"]
@@ -411,7 +411,7 @@ def main(
411
411
  ) -> None:
412
412
  init_env(log_level=log_level, config_file=configfile)
413
413
 
414
- now = datetime.now()
414
+ now = utc_now()
415
415
  apps = get_apps_data(now, thread_pool_size=thread_pool_size)
416
416
 
417
417
  reports = [Report(app, now).to_message() for app in apps]
@@ -74,7 +74,7 @@ class CostManagementApi(ApiBase):
74
74
  timeout=self.read_timeout,
75
75
  )
76
76
  response.raise_for_status()
77
- return AwsReportCostResponse.parse_obj(response.json())
77
+ return AwsReportCostResponse.model_validate(response.json())
78
78
 
79
79
  def get_openshift_costs_report(
80
80
  self,
@@ -97,7 +97,7 @@ class CostManagementApi(ApiBase):
97
97
  timeout=self.read_timeout,
98
98
  )
99
99
  response.raise_for_status()
100
- return OpenShiftReportCostResponse.parse_obj(response.json())
100
+ return OpenShiftReportCostResponse.model_validate(response.json())
101
101
 
102
102
  def get_openshift_cost_optimization_report(
103
103
  self,
@@ -120,7 +120,7 @@ class CostManagementApi(ApiBase):
120
120
  response.raise_for_status()
121
121
 
122
122
  data = self._get_paginated(response)
123
- return OpenShiftCostOptimizationReportResponse.parse_obj(data)
123
+ return OpenShiftCostOptimizationReportResponse.model_validate(data)
124
124
 
125
125
  def _get_paginated(
126
126
  self,
@@ -4,6 +4,7 @@ from typing import Any
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
+ from reconcile.utils.json import json_dumps
7
8
  from tools.cli_commands.cost_report.model import OptimizationReport, Report
8
9
 
9
10
  LAYOUT = """\
@@ -244,7 +245,7 @@ def render_summary(
244
245
  return template.format(
245
246
  date=get_date(reports),
246
247
  total_cost=format_cost_value(total_cost),
247
- json_table=json_table.json(indent=2),
248
+ json_table=json_dumps(json_table, indent=2, mode="python"),
248
249
  )
249
250
 
250
251
 
@@ -274,7 +275,7 @@ def render_month_over_month_change(reports: Mapping[str, Report]) -> str:
274
275
  )
275
276
  return MONTH_OVER_MONTH_CHANGE.format(
276
277
  date=get_date(reports),
277
- json_table=json_table.json(indent=2),
278
+ json_table=json_dumps(json_table, indent=2, mode="python"),
278
279
  )
279
280
 
280
281
 
@@ -304,7 +305,7 @@ def render_aws_services_cost(
304
305
  items_total=format_cost_value(report.items_total),
305
306
  items_delta_value=format_delta_value(report.items_delta_value),
306
307
  items_delta_percent=format_delta_percent(report.items_delta_percent),
307
- json_table=json_table.json(indent=2),
308
+ json_table=json_dumps(json_table, indent=2, mode="python"),
308
309
  )
309
310
 
310
311
 
@@ -316,7 +317,7 @@ def render_openshift_workloads_cost(
316
317
  items_total=format_cost_value(report.items_total),
317
318
  items_delta_value=format_delta_value(report.items_delta_value),
318
319
  items_delta_percent=format_delta_percent(report.items_delta_percent),
319
- json_table=json_table.json(indent=2),
320
+ json_table=json_dumps(json_table, indent=2, mode="python"),
320
321
  )
321
322
 
322
323
 
@@ -362,7 +363,7 @@ def render_child_apps_cost(report: Report) -> str:
362
363
  )
363
364
  return CHILD_APPS_COST.format(
364
365
  child_apps_total=format_cost_value(report.child_apps_total),
365
- json_table=json_table.json(indent=2),
366
+ json_table=json_dumps(json_table, indent=2, mode="python"),
366
367
  )
367
368
 
368
369
 
@@ -509,7 +510,7 @@ def render_optimization(
509
510
  )
510
511
  return OPTIMIZATION.format(
511
512
  app_name=report.app_name,
512
- json_table=json_table.json(indent=2),
513
+ json_table=json_dumps(json_table, indent=2, mode="python"),
513
514
  )
514
515
 
515
516
 
@@ -133,7 +133,7 @@ class Erv2Cli:
133
133
 
134
134
  @property
135
135
  def input_data(self) -> str:
136
- return self._resource.json(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}})
136
+ return self._resource.export(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}})
137
137
 
138
138
  @property
139
139
  def image(self) -> str:
@@ -132,6 +132,7 @@ from reconcile.typed_queries.vault import get_vault_instances
132
132
  from reconcile.utils import (
133
133
  gql,
134
134
  )
135
+ from reconcile.utils.slack_api import is_gov_slack_workspace
135
136
 
136
137
 
137
138
  class SystemTool(BaseModel):
@@ -322,11 +323,14 @@ class SystemTool(BaseModel):
322
323
 
323
324
  @classmethod
324
325
  def init_from_slack_workspace(cls, s: SlackWorkspaceV1, enumeration: Any) -> Self:
326
+ # Automatically determine the correct Slack domain based on GOV_SLACK environment variable
327
+ domain = "slack-gov.com" if is_gov_slack_workspace() else "slack.com"
328
+
325
329
  return cls(
326
330
  system_type="slack",
327
331
  system_id=s.name,
328
332
  name=s.name,
329
- url=f"https://{s.name}.slack.com",
333
+ url=f"https://{s.name}.{domain}",
330
334
  description=s.description,
331
335
  enumeration=enumeration,
332
336
  )
tools/qontract_cli.py CHANGED
@@ -13,7 +13,6 @@ import tempfile
13
13
  import textwrap
14
14
  from collections import defaultdict
15
15
  from datetime import (
16
- UTC,
17
16
  datetime,
18
17
  timedelta,
19
18
  )
@@ -122,6 +121,7 @@ from reconcile.utils.binary import (
122
121
  binary,
123
122
  binary_version,
124
123
  )
124
+ from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
125
125
  from reconcile.utils.early_exit_cache import (
126
126
  CacheKey,
127
127
  CacheKeyWithDigest,
@@ -141,6 +141,7 @@ from reconcile.utils.gitlab_api import (
141
141
  )
142
142
  from reconcile.utils.glitchtip.client import GlitchtipClient
143
143
  from reconcile.utils.gql import GqlApiSingleton
144
+ from reconcile.utils.json import json_dumps
144
145
  from reconcile.utils.keycloak import (
145
146
  KeycloakAPI,
146
147
  SSOClient,
@@ -417,8 +418,8 @@ def get_upgrade_policies_data(
417
418
  upgrade_next_run = None
418
419
  upgrade_emoji = "💫"
419
420
  if upgrade_next_run:
420
- dt = datetime.strptime(upgrade_next_run, "%Y-%m-%dT%H:%M:%SZ")
421
- now = datetime.utcnow()
421
+ dt = from_utc_iso_format(upgrade_next_run)
422
+ now = utc_now()
422
423
  if dt > now:
423
424
  upgrade_emoji = "⏰"
424
425
  hours_ago = (now - dt).total_seconds() / 3600
@@ -841,7 +842,7 @@ def alert_report(
841
842
  )
842
843
  sys.exit(1)
843
844
 
844
- now = datetime.utcnow()
845
+ now = utc_now()
845
846
  from_timestamp = int((now - timedelta(days=days)).timestamp())
846
847
  to_timestamp = int(now.timestamp())
847
848
 
@@ -887,7 +888,9 @@ def alert_report(
887
888
  "Triggered": str(data.triggered_alerts),
888
889
  "Resolved": str(data.resolved_alerts),
889
890
  "Median time to resolve (h:mm:ss)": median_elapsed,
890
- "Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%",
891
+ "Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%"
892
+ if data.triggered_alerts != 0
893
+ else "0.00%",
891
894
  })
892
895
 
893
896
  # TODO(mafriedm, rporres): Fix this
@@ -1564,7 +1567,7 @@ def rosa_create_cluster_command(ctx: click.Context, cluster_name: str) -> None:
1564
1567
  billing_account = account.billing_account.uid
1565
1568
  else:
1566
1569
  with AWSApi(
1567
- 1, [account.dict(by_alias=True)], settings=settings, init_users=False
1570
+ 1, [account.model_dump(by_alias=True)], settings=settings, init_users=False
1568
1571
  ) as aws_api:
1569
1572
  billing_account = aws_api.get_organization_billing_account(account.name)
1570
1573
 
@@ -1748,7 +1751,7 @@ def aws_terraform_resources(ctx: click.Context) -> None:
1748
1751
  for ns_info in namespaces:
1749
1752
  specs = (
1750
1753
  get_external_resource_specs(
1751
- ns_info.dict(by_alias=True), provision_provider=PROVIDER_AWS
1754
+ ns_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
1752
1755
  )
1753
1756
  or []
1754
1757
  )
@@ -1806,7 +1809,7 @@ def rds(ctx: click.Context) -> None:
1806
1809
  specs = [
1807
1810
  s
1808
1811
  for s in get_external_resource_specs(
1809
- namespace.dict(by_alias=True), provision_provider=PROVIDER_AWS
1812
+ namespace.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
1810
1813
  )
1811
1814
  if s.provider == "rds"
1812
1815
  ]
@@ -2272,7 +2275,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
2272
2275
  "labels",
2273
2276
  ]
2274
2277
  merge_queue_data = []
2275
- now = datetime.utcnow()
2278
+ now = utc_now()
2276
2279
  for mr in merge_requests:
2277
2280
  item = {
2278
2281
  "id": f"[{mr['mr'].iid}]({mr['mr'].web_url})",
@@ -2281,7 +2284,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
2281
2284
  + 1, # adding 1 for human readability
2282
2285
  "approved_at": mr["approved_at"],
2283
2286
  "approved_span_minutes": (
2284
- now - datetime.strptime(mr["approved_at"], glhk.DATE_FORMAT)
2287
+ now - from_utc_iso_format(mr["approved_at"])
2285
2288
  ).total_seconds()
2286
2289
  / 60,
2287
2290
  "approved_by": mr["approved_by"],
@@ -2695,7 +2698,7 @@ def ec2_jenkins_workers(
2695
2698
  client = boto3.client("autoscaling")
2696
2699
  ec2 = boto3.resource("ec2")
2697
2700
  results = []
2698
- now = datetime.now(UTC)
2701
+ now = utc_now()
2699
2702
  columns = [
2700
2703
  "type",
2701
2704
  "id",
@@ -2955,11 +2958,11 @@ def osd_component_versions(ctx: click.Context) -> None:
2955
2958
  @get.command()
2956
2959
  @click.pass_context
2957
2960
  def maintenances(ctx: click.Context) -> None:
2958
- now = datetime.now(UTC)
2961
+ now = utc_now()
2959
2962
  maintenances = maintenances_gql.query(gql.get_api().query).maintenances or []
2960
2963
  data = [
2961
2964
  {
2962
- **m.dict(),
2965
+ **m.model_dump(),
2963
2966
  "services": ", ".join(a.name for a in m.affected_services),
2964
2967
  }
2965
2968
  for m in maintenances
@@ -4097,7 +4100,9 @@ def sre_checkpoint_metadata(
4097
4100
  ) -> None:
4098
4101
  """Check an app path for checkpoint-related metadata."""
4099
4102
  data = queries.get_app_metadata(app_path)
4100
- settings = queries.get_app_interface_settings()
4103
+ vault_settings = get_app_interface_vault_settings()
4104
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
4105
+
4101
4106
  app = data[0]
4102
4107
 
4103
4108
  if jiradef:
@@ -4110,7 +4115,14 @@ def sre_checkpoint_metadata(
4110
4115
  # Overrides for easier testing
4111
4116
  if jiraboard:
4112
4117
  board["name"] = jiraboard
4113
- report_invalid_metadata(app, app_path, board, settings, parent_ticket, dry_run)
4118
+ report_invalid_metadata(
4119
+ app=app,
4120
+ path=app_path,
4121
+ board=board,
4122
+ secret_reader=secret_reader,
4123
+ parent=parent_ticket,
4124
+ dry_run=dry_run,
4125
+ )
4114
4126
 
4115
4127
 
4116
4128
  @root.command()
@@ -4287,7 +4299,7 @@ def create(
4287
4299
  bg="red",
4288
4300
  fg="white",
4289
4301
  )
4290
- print(sso_client.json(by_alias=True, indent=2))
4302
+ print(json_dumps(sso_client, indent=2))
4291
4303
 
4292
4304
 
4293
4305
  @sso_client.command()
@@ -4839,11 +4851,12 @@ def top_talkers(ctx: click.Context, top: int) -> None:
4839
4851
  assert project.organization # make mypy happy
4840
4852
  assert project.pk # make mypy happy
4841
4853
 
4854
+ now = utc_now()
4842
4855
  stat = client.project_statistics(
4843
4856
  organization_slug=project.organization.slug,
4844
4857
  project_pk=project.pk,
4845
- start=datetime.now(tz=UTC) - timedelta(hours=24),
4846
- end=datetime.now(tz=UTC),
4858
+ start=now - timedelta(hours=24),
4859
+ end=now,
4847
4860
  )
4848
4861
  stats.append((project, stat))
4849
4862
 
@@ -67,7 +67,9 @@ def main(templates: tuple[str]) -> None:
67
67
  tests.append(test_yaml)
68
68
 
69
69
  template_raw["templateTest"] = tests
70
- template: TemplateV1 = TemplateV1(**data_default_none(TemplateV1, template_raw))
70
+ data = data_default_none(TemplateV1, template_raw)
71
+ assert isinstance(data, dict)
72
+ template: TemplateV1 = TemplateV1(**data)
71
73
 
72
74
  # templates_to_validate = {}
73
75
  for test in template.template_test:
File without changes
File without changes
File without changes