qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev474__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 (371) hide show
  1. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/METADATA +14 -13
  2. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/RECORD +371 -364
  3. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/WHEEL +1 -1
  4. reconcile/acs_rbac.py +2 -2
  5. reconcile/aus/advanced_upgrade_service.py +18 -12
  6. reconcile/aus/aus_sts_gate_handler.py +59 -0
  7. reconcile/aus/base.py +137 -34
  8. reconcile/aus/cluster_version_data.py +15 -5
  9. reconcile/aus/models.py +3 -1
  10. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  11. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  12. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  13. reconcile/aus/version_gate_approver.py +1 -16
  14. reconcile/aus/version_gates/sts_version_gate_handler.py +5 -72
  15. reconcile/automated_actions/config/integration.py +16 -4
  16. reconcile/aws_account_manager/integration.py +21 -9
  17. reconcile/aws_account_manager/reconciler.py +3 -3
  18. reconcile/aws_account_manager/utils.py +1 -1
  19. reconcile/aws_ami_cleanup/integration.py +8 -12
  20. reconcile/aws_ami_share.py +69 -62
  21. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  22. reconcile/aws_ecr_image_pull_secrets.py +1 -1
  23. reconcile/aws_iam_keys.py +1 -0
  24. reconcile/aws_saml_idp/integration.py +12 -4
  25. reconcile/aws_saml_roles/integration.py +30 -23
  26. reconcile/aws_version_sync/integration.py +6 -12
  27. reconcile/change_owners/README.md +1 -1
  28. reconcile/change_owners/bundle.py +3 -3
  29. reconcile/change_owners/change_log_tracking.py +3 -2
  30. reconcile/change_owners/change_owners.py +108 -42
  31. reconcile/change_owners/decision.py +1 -1
  32. reconcile/change_owners/diff.py +0 -2
  33. reconcile/checkpoint.py +11 -3
  34. reconcile/cli.py +94 -11
  35. reconcile/dashdotdb_dora.py +5 -12
  36. reconcile/dashdotdb_slo.py +1 -1
  37. reconcile/database_access_manager.py +123 -117
  38. reconcile/dynatrace_token_provider/integration.py +1 -1
  39. reconcile/endpoints_discovery/integration.py +4 -1
  40. reconcile/endpoints_discovery/merge_request.py +1 -1
  41. reconcile/endpoints_discovery/merge_request_manager.py +8 -8
  42. reconcile/external_resources/factories.py +4 -6
  43. reconcile/external_resources/integration.py +1 -1
  44. reconcile/external_resources/manager.py +8 -6
  45. reconcile/external_resources/meta.py +0 -1
  46. reconcile/external_resources/metrics.py +1 -1
  47. reconcile/external_resources/model.py +19 -15
  48. reconcile/external_resources/reconciler.py +7 -4
  49. reconcile/external_resources/secrets_sync.py +6 -10
  50. reconcile/external_resources/state.py +26 -16
  51. reconcile/fleet_labeler/integration.py +1 -1
  52. reconcile/gabi_authorized_users.py +5 -2
  53. reconcile/gcp_image_mirror.py +2 -2
  54. reconcile/github_org.py +1 -1
  55. reconcile/github_owners.py +4 -0
  56. reconcile/gitlab_housekeeping.py +13 -15
  57. reconcile/gitlab_members.py +6 -12
  58. reconcile/gitlab_owners.py +15 -11
  59. reconcile/gitlab_permissions.py +8 -12
  60. reconcile/glitchtip_project_alerts/integration.py +3 -1
  61. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  62. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  63. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  64. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  65. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  66. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  67. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  68. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  69. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  70. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +14 -5
  71. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  72. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  73. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  74. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  75. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  76. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  77. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  78. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  79. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  80. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  81. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  84. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  85. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  87. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  88. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  89. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  90. reconcile/gql_definitions/common/apps.py +5 -5
  91. reconcile/gql_definitions/common/aws_vpc_requests.py +18 -5
  92. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  93. reconcile/gql_definitions/common/clusters.py +7 -5
  94. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  95. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  96. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  97. reconcile/gql_definitions/common/github_orgs.py +5 -5
  98. reconcile/gql_definitions/common/jira_settings.py +5 -5
  99. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  100. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  101. reconcile/gql_definitions/common/namespaces.py +5 -5
  102. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  103. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  104. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  105. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  106. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  107. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  108. reconcile/gql_definitions/common/quay_instances.py +5 -5
  109. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  110. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  111. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  112. reconcile/gql_definitions/common/saas_files.py +5 -5
  113. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  114. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  115. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  116. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  117. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  118. reconcile/gql_definitions/common/users.py +5 -5
  119. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  120. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  121. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  122. reconcile/gql_definitions/cost_report/settings.py +5 -5
  123. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  124. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  125. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  126. reconcile/gql_definitions/email_sender/apps.py +5 -5
  127. reconcile/gql_definitions/email_sender/emails.py +5 -5
  128. reconcile/gql_definitions/email_sender/users.py +5 -5
  129. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  130. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  131. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  132. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +38 -7
  133. reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
  134. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  135. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  136. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  138. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  139. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  141. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  142. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  143. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  144. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  145. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  146. reconcile/gql_definitions/fragments/disable.py +5 -5
  147. reconcile/gql_definitions/fragments/email_service.py +5 -5
  148. reconcile/gql_definitions/fragments/email_user.py +5 -5
  149. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  150. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  151. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  152. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  153. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  154. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  155. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  156. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  157. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  158. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  159. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  160. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  161. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  162. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  163. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  164. reconcile/gql_definitions/fragments/user.py +5 -5
  165. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  166. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  167. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  168. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  169. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  170. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  171. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  172. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  173. reconcile/gql_definitions/integrations/integrations.py +5 -5
  174. reconcile/gql_definitions/introspection.json +775 -136
  175. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  176. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  177. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  178. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  179. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  180. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  181. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  182. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  183. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  184. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  185. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  186. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  187. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  188. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  189. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  190. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  191. reconcile/gql_definitions/rhcs/certs.py +25 -79
  192. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  193. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  194. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  195. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  196. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  197. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  198. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  199. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  200. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  201. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  202. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  203. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  204. reconcile/gql_definitions/status_board/status_board.py +5 -5
  205. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  206. reconcile/gql_definitions/templating/template_collection.py +5 -5
  207. reconcile/gql_definitions/templating/templates.py +5 -5
  208. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  209. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  210. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  211. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  212. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  213. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  214. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  215. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  216. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  217. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +37 -7
  218. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  219. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  220. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  221. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  222. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -5
  223. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +6 -5
  224. reconcile/integrations_manager.py +3 -3
  225. reconcile/jenkins_worker_fleets.py +10 -8
  226. reconcile/jira_permissions_validator.py +237 -122
  227. reconcile/ldap_groups/integration.py +1 -1
  228. reconcile/ocm/types.py +35 -57
  229. reconcile/ocm_aws_infrastructure_access.py +1 -1
  230. reconcile/ocm_clusters.py +4 -4
  231. reconcile/ocm_labels/integration.py +3 -2
  232. reconcile/ocm_machine_pools.py +33 -27
  233. reconcile/openshift_base.py +113 -4
  234. reconcile/openshift_cluster_bots.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +96 -101
  237. reconcile/openshift_resources_base.py +6 -2
  238. reconcile/openshift_rhcs_certs.py +74 -37
  239. reconcile/openshift_rolebindings.py +7 -11
  240. reconcile/openshift_saas_deploy.py +4 -5
  241. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  242. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  243. reconcile/openshift_serviceaccount_tokens.py +2 -2
  244. reconcile/openshift_upgrade_watcher.py +4 -4
  245. reconcile/oum/labelset.py +5 -3
  246. reconcile/oum/models.py +1 -4
  247. reconcile/prometheus_rules_tester/integration.py +3 -3
  248. reconcile/quay_base.py +25 -6
  249. reconcile/quay_membership.py +55 -29
  250. reconcile/quay_mirror.py +1 -1
  251. reconcile/quay_mirror_org.py +6 -4
  252. reconcile/quay_permissions.py +81 -75
  253. reconcile/quay_repos.py +35 -37
  254. reconcile/queries.py +132 -1
  255. reconcile/rhidp/common.py +3 -5
  256. reconcile/rhidp/sso_client/base.py +16 -5
  257. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  258. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  259. reconcile/skupper_network/integration.py +2 -2
  260. reconcile/slack_usergroups.py +35 -14
  261. reconcile/sql_query.py +1 -0
  262. reconcile/status_board.py +6 -6
  263. reconcile/statuspage/atlassian.py +7 -7
  264. reconcile/statuspage/integrations/maintenances.py +4 -3
  265. reconcile/statuspage/page.py +4 -9
  266. reconcile/statuspage/status.py +5 -8
  267. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +1 -1
  268. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +1 -1
  269. reconcile/templating/lib/rendering.py +3 -3
  270. reconcile/templating/renderer.py +2 -2
  271. reconcile/templating/validator.py +4 -4
  272. reconcile/terraform_aws_route53.py +7 -1
  273. reconcile/terraform_cloudflare_dns.py +3 -3
  274. reconcile/terraform_cloudflare_resources.py +5 -5
  275. reconcile/terraform_cloudflare_users.py +3 -2
  276. reconcile/terraform_init/integration.py +187 -23
  277. reconcile/terraform_repo.py +16 -12
  278. reconcile/terraform_resources.py +6 -6
  279. reconcile/terraform_tgw_attachments.py +27 -19
  280. reconcile/terraform_users.py +7 -0
  281. reconcile/terraform_vpc_peerings.py +14 -3
  282. reconcile/terraform_vpc_resources/integration.py +20 -8
  283. reconcile/terraform_vpc_resources/merge_request.py +12 -2
  284. reconcile/terraform_vpc_resources/merge_request_manager.py +43 -19
  285. reconcile/typed_queries/aws_account_tags.py +41 -0
  286. reconcile/typed_queries/cost_report/app_names.py +1 -1
  287. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  288. reconcile/typed_queries/saas_files.py +20 -15
  289. reconcile/typed_queries/status_board.py +2 -2
  290. reconcile/unleash_feature_toggles/integration.py +4 -2
  291. reconcile/utils/acs/base.py +6 -3
  292. reconcile/utils/acs/policies.py +2 -2
  293. reconcile/utils/aws_api.py +51 -20
  294. reconcile/utils/aws_api_typed/api.py +38 -9
  295. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  296. reconcile/utils/aws_api_typed/logs.py +73 -0
  297. reconcile/utils/aws_api_typed/organization.py +4 -2
  298. reconcile/utils/binary.py +7 -12
  299. reconcile/utils/datetime_util.py +67 -0
  300. reconcile/utils/deadmanssnitch_api.py +1 -1
  301. reconcile/utils/differ.py +2 -3
  302. reconcile/utils/early_exit_cache.py +11 -12
  303. reconcile/utils/environ.py +5 -0
  304. reconcile/utils/expiration.py +7 -3
  305. reconcile/utils/external_resource_spec.py +2 -0
  306. reconcile/utils/filtering.py +1 -1
  307. reconcile/utils/gitlab_api.py +19 -5
  308. reconcile/utils/glitchtip/client.py +6 -2
  309. reconcile/utils/glitchtip/models.py +25 -28
  310. reconcile/utils/gql.py +4 -7
  311. reconcile/utils/helpers.py +1 -1
  312. reconcile/utils/instrumented_wrappers.py +1 -1
  313. reconcile/utils/internal_groups/client.py +2 -2
  314. reconcile/utils/internal_groups/models.py +8 -17
  315. reconcile/utils/jinja2/utils.py +6 -101
  316. reconcile/utils/jira_client.py +82 -63
  317. reconcile/utils/jjb_client.py +26 -13
  318. reconcile/utils/jobcontroller/controller.py +2 -2
  319. reconcile/utils/jobcontroller/models.py +17 -1
  320. reconcile/utils/json.py +43 -1
  321. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  322. reconcile/utils/membershipsources/models.py +16 -23
  323. reconcile/utils/membershipsources/resolver.py +4 -2
  324. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  325. reconcile/utils/merge_request_manager/parser.py +6 -6
  326. reconcile/utils/metrics.py +5 -5
  327. reconcile/utils/models.py +304 -82
  328. reconcile/utils/mr/app_interface_reporter.py +2 -2
  329. reconcile/utils/mr/notificator.py +3 -3
  330. reconcile/utils/mr/update_access_report_base.py +3 -4
  331. reconcile/utils/mr/user_maintenance.py +3 -2
  332. reconcile/utils/oc.py +252 -201
  333. reconcile/utils/oc_filters.py +3 -3
  334. reconcile/utils/ocm/addons.py +0 -1
  335. reconcile/utils/ocm/base.py +17 -20
  336. reconcile/utils/ocm/cluster_groups.py +1 -1
  337. reconcile/utils/ocm/identity_providers.py +2 -2
  338. reconcile/utils/ocm/labels.py +1 -1
  339. reconcile/utils/ocm/products.py +8 -8
  340. reconcile/utils/ocm/search_filters.py +3 -6
  341. reconcile/utils/ocm/service_log.py +4 -6
  342. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  343. reconcile/utils/openshift_resource.py +8 -3
  344. reconcile/utils/pagerduty_api.py +10 -7
  345. reconcile/utils/promotion_state.py +6 -11
  346. reconcile/utils/quay_api.py +74 -87
  347. reconcile/utils/raw_github_api.py +1 -1
  348. reconcile/utils/rhcsv2_certs.py +86 -23
  349. reconcile/utils/rosa/session.py +16 -0
  350. reconcile/utils/runtime/integration.py +2 -3
  351. reconcile/utils/runtime/runner.py +2 -2
  352. reconcile/utils/saasherder/interfaces.py +13 -20
  353. reconcile/utils/saasherder/models.py +23 -20
  354. reconcile/utils/saasherder/saasherder.py +50 -27
  355. reconcile/utils/slack_api.py +2 -2
  356. reconcile/utils/sloth.py +171 -2
  357. reconcile/utils/structs.py +1 -1
  358. reconcile/utils/terraform_client.py +5 -4
  359. reconcile/utils/terrascript_aws_client.py +274 -124
  360. reconcile/utils/unleash/server.py +2 -8
  361. reconcile/utils/vault.py +5 -12
  362. reconcile/utils/vcs.py +8 -8
  363. reconcile/vault_replication.py +107 -42
  364. reconcile/vpc_peerings_validator.py +13 -0
  365. tools/app_interface_reporter.py +4 -4
  366. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  367. tools/cli_commands/cost_report/view.py +7 -6
  368. tools/cli_commands/erv2.py +1 -1
  369. tools/qontract_cli.py +28 -17
  370. tools/template_validation.py +3 -1
  371. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/entry_points.txt +0 -0
@@ -7,10 +7,7 @@ from enum import Enum
7
7
  from typing import Any, NotRequired, TypedDict
8
8
 
9
9
  from github import Github
10
- from pydantic import (
11
- BaseModel,
12
- Field,
13
- )
10
+ from pydantic import BaseModel, Field, model_validator
14
11
 
15
12
  from reconcile.gql_definitions.fragments.saas_slo_document import (
16
13
  SLODocument,
@@ -207,7 +204,12 @@ TriggerSpecUnion = (
207
204
  )
208
205
 
209
206
 
210
- class Namespace(BaseModel):
207
+ class Namespace(
208
+ BaseModel,
209
+ validate_by_name=True,
210
+ validate_by_alias=True,
211
+ arbitrary_types_allowed=True,
212
+ ):
211
213
  name: str
212
214
  environment: SaasEnvironment
213
215
  app: SaasApp
@@ -217,29 +219,19 @@ class Namespace(BaseModel):
217
219
  ..., alias="managedResourceNames"
218
220
  )
219
221
 
220
- class Config:
221
- arbitrary_types_allowed = True
222
- allow_population_by_field_name = True
223
-
224
222
 
225
- class PromotionChannelData(BaseModel):
223
+ class PromotionChannelData(BaseModel, validate_by_name=True, validate_by_alias=True):
226
224
  q_type: str = Field(..., alias="type")
227
225
 
228
- class Config:
229
- allow_population_by_field_name = True
230
226
 
231
-
232
- class ParentSaasPromotion(BaseModel):
227
+ class ParentSaasPromotion(BaseModel, validate_by_name=True, validate_by_alias=True):
233
228
  q_type: str = Field(..., alias="type")
234
- parent_saas: str | None
235
- target_config_hash: str | None
236
-
237
- class Config:
238
- allow_population_by_field_name = True
229
+ parent_saas: str | None = None
230
+ target_config_hash: str | None = None
239
231
 
240
232
 
241
233
  class PromotionData(BaseModel):
242
- channel: str | None
234
+ channel: str | None = None
243
235
  data: list[ParentSaasPromotion | PromotionChannelData] | None = None
244
236
 
245
237
 
@@ -264,6 +256,17 @@ class Promotion(BaseModel):
264
256
  saas_file_paths: list[str] | None = None
265
257
  target_paths: list[str] | None = None
266
258
 
259
+ @model_validator(mode="before")
260
+ @classmethod
261
+ def handle_gql_classes(cls, data: Any) -> Any:
262
+ if data.get("promotion_data"):
263
+ data["promotion_data"] = [
264
+ # convert a GQL class to a dict
265
+ item.model_dump() if hasattr(item, "model_dump") else item
266
+ for item in data["promotion_data"]
267
+ ]
268
+ return data
269
+
267
270
 
268
271
  @dataclass
269
272
  class ImageAuth:
@@ -16,7 +16,7 @@ from collections.abc import (
16
16
  Sequence,
17
17
  )
18
18
  from contextlib import suppress
19
- from datetime import UTC, datetime, timedelta
19
+ from datetime import datetime, timedelta
20
20
  from types import TracebackType
21
21
  from typing import Any
22
22
 
@@ -37,6 +37,7 @@ from sretoolbox.utils import (
37
37
  from reconcile.github_org import get_default_config
38
38
  from reconcile.status import RunningState
39
39
  from reconcile.utils import helm
40
+ from reconcile.utils.datetime_util import utc_now
40
41
  from reconcile.utils.github_api import GithubRepositoryApi
41
42
  from reconcile.utils.gitlab_api import GitLabApi
42
43
  from reconcile.utils.jenkins_api import JenkinsApi, JobBuildState
@@ -91,8 +92,7 @@ from reconcile.utils.state import State
91
92
  from reconcile.utils.vcs import VCS
92
93
 
93
94
  TARGET_CONFIG_HASH = "target_config_hash"
94
-
95
-
95
+ TEMPLATE_API_VERSION = "template.openshift.io/v1"
96
96
  UNIQUE_SAAS_FILE_ENV_COMBO_LEN = 56
97
97
  REQUEST_TIMEOUT = 60
98
98
 
@@ -764,7 +764,8 @@ class SaasHerder:
764
764
  case "gitlab":
765
765
  if not self.gitlab:
766
766
  raise Exception("gitlab is not initialized")
767
- project = self.gitlab.get_project(url)
767
+ if not (project := self.gitlab.get_project(url)):
768
+ raise Exception(f"Could not find gitlab project for {url}")
768
769
  content = self.gitlab.get_raw_file(
769
770
  project=project,
770
771
  path=path,
@@ -800,7 +801,8 @@ class SaasHerder:
800
801
  case "gitlab":
801
802
  if not self.gitlab:
802
803
  raise Exception("gitlab is not initialized")
803
- project = self.gitlab.get_project(url)
804
+ if not (project := self.gitlab.get_project(url)):
805
+ raise Exception(f"Could not find gitlab project for {url}")
804
806
  dir_contents = self.gitlab.get_directory_contents(
805
807
  project,
806
808
  ref=commit_sha,
@@ -825,7 +827,8 @@ class SaasHerder:
825
827
  case "gitlab":
826
828
  if not self.gitlab:
827
829
  raise Exception("gitlab is not initialized")
828
- project = self.gitlab.get_project(url)
830
+ if not (project := self.gitlab.get_project(url)):
831
+ raise Exception(f"Could not find gitlab project for {url}")
829
832
  commits = project.commits.list(ref_name=ref, per_page=1, page=1)
830
833
  return commits[0].id
831
834
  case _:
@@ -870,10 +873,23 @@ class SaasHerder:
870
873
  """
871
874
  if parameter_name in consolidated_parameters:
872
875
  return False
873
- for template_parameter in template.get("parameters", {}):
874
- if template_parameter["name"] == parameter_name:
875
- return True
876
- return False
876
+ return any(
877
+ template_parameter["name"] == parameter_name
878
+ for template_parameter in template.get("parameters") or []
879
+ )
880
+
881
+ @staticmethod
882
+ def _pre_process_template(template: dict[str, Any]) -> dict[str, Any]:
883
+ """
884
+ The only supported apiVersion for OpenShift Template is "template.openshift.io/v1".
885
+ There are examples of templates using "v1", it can't pass validation on 4.19+ oc versions.
886
+
887
+ Args:
888
+ template (dict): The OpenShift template dictionary.
889
+ Returns:
890
+ dict: The OpenShift template dictionary with the correct apiVersion.
891
+ """
892
+ return template | {"apiVersion": TEMPLATE_API_VERSION}
877
893
 
878
894
  def _process_template(
879
895
  self, spec: TargetSpec
@@ -963,7 +979,8 @@ class SaasHerder:
963
979
  oc = OCLocal("cluster", None, None, local=True)
964
980
  try:
965
981
  resources: Iterable[Mapping[str, Any]] = oc.process(
966
- template, consolidated_parameters
982
+ template=self._pre_process_template(template),
983
+ parameters=consolidated_parameters,
967
984
  )
968
985
  except StatusCodeError as e:
969
986
  logging.error(f"{error_prefix} error processing template: {e!s}")
@@ -1178,13 +1195,13 @@ class SaasHerder:
1178
1195
  images_list = threaded.run(
1179
1196
  self._collect_images, resources, self.available_thread_pool_size
1180
1197
  )
1181
- images = set(itertools.chain.from_iterable(images_list))
1182
- self.images.update(images)
1183
- if not images:
1198
+ images_set = set(itertools.chain.from_iterable(images_list))
1199
+ self.images.update(images_set)
1200
+ if not images_set:
1184
1201
  return False # no errors
1185
1202
  images = threaded.run(
1186
1203
  self._get_image,
1187
- images,
1204
+ images_set,
1188
1205
  self.available_thread_pool_size,
1189
1206
  image_patterns=spec.image_patterns,
1190
1207
  image_auth=spec.image_auth,
@@ -1249,7 +1266,9 @@ class SaasHerder:
1249
1266
  self.saas_files,
1250
1267
  self.thread_pool_size,
1251
1268
  )
1252
- desired_state_specs = list(itertools.chain.from_iterable(results))
1269
+ desired_state_specs: list[TargetSpec] = list(
1270
+ itertools.chain.from_iterable(results)
1271
+ )
1253
1272
  promotions = threaded.run(
1254
1273
  self.populate_desired_state_saas_file,
1255
1274
  desired_state_specs,
@@ -1897,21 +1916,23 @@ class SaasHerder:
1897
1916
  name=target.name,
1898
1917
  ref=target.ref,
1899
1918
  promotion=(
1900
- target.promotion.dict(by_alias=True) if target.promotion else None
1919
+ target.promotion.model_dump(by_alias=True) if target.promotion else None
1901
1920
  ),
1902
1921
  secretParameters=(
1903
- [p.dict(by_alias=True) for p in target.secret_parameters]
1922
+ [p.model_dump(by_alias=True) for p in target.secret_parameters]
1904
1923
  if target.secret_parameters
1905
1924
  else None
1906
1925
  ),
1907
1926
  slos=(
1908
- [slo.dict(by_alias=True) for slo in target.slos]
1927
+ [slo.model_dump(by_alias=True) for slo in target.slos]
1909
1928
  if target.slos
1910
1929
  else None
1911
1930
  ),
1912
- upstream=(target.upstream.dict(by_alias=True) if target.upstream else None),
1931
+ upstream=(
1932
+ target.upstream.model_dump(by_alias=True) if target.upstream else None
1933
+ ),
1913
1934
  images=(
1914
- [i.dict(by_alias=True) for i in target.images]
1935
+ [i.model_dump(by_alias=True) for i in target.images]
1915
1936
  if target.images
1916
1937
  else None
1917
1938
  ),
@@ -1947,16 +1968,16 @@ class SaasHerder:
1947
1968
  )
1948
1969
  if saas_file.managed_resource_names:
1949
1970
  state_content["saas_file_managed_resource_names"] = [
1950
- m.dict() for m in saas_file.managed_resource_names
1971
+ m.model_dump() for m in saas_file.managed_resource_names
1951
1972
  ]
1952
1973
  # include secret parameters from resource template and saas file
1953
1974
  if resource_template.secret_parameters:
1954
1975
  state_content["rt_secretparameters"] = [
1955
- p.dict() for p in resource_template.secret_parameters
1976
+ p.model_dump() for p in resource_template.secret_parameters
1956
1977
  ]
1957
1978
  if saas_file.secret_parameters:
1958
1979
  state_content["saas_file_secretparameters"] = [
1959
- p.dict() for p in saas_file.secret_parameters
1980
+ p.model_dump() for p in saas_file.secret_parameters
1960
1981
  ]
1961
1982
  return state_content
1962
1983
 
@@ -2025,7 +2046,7 @@ class SaasHerder:
2025
2046
  if promotion.commit_sha in self.hotfix_versions.get(promotion.url, set()):
2026
2047
  return True
2027
2048
 
2028
- now = datetime.now(UTC)
2049
+ now = utc_now()
2029
2050
  passed_soak_days = timedelta(days=0)
2030
2051
 
2031
2052
  for channel in promotion.subscribe:
@@ -2127,7 +2148,7 @@ class SaasHerder:
2127
2148
  if not (self.state and self._promotion_state):
2128
2149
  raise Exception("state is not initialized")
2129
2150
 
2130
- now = datetime.now(UTC)
2151
+ now = utc_now()
2131
2152
  for promotion in self.promotions:
2132
2153
  if promotion is None:
2133
2154
  continue
@@ -2239,7 +2260,9 @@ class SaasHerder:
2239
2260
  for rt in saas_file.resource_templates:
2240
2261
  for target in rt.targets:
2241
2262
  template_vars = {
2242
- "resource": {"namespace": target.namespace.dict(by_alias=True)}
2263
+ "resource": {
2264
+ "namespace": target.namespace.model_dump(by_alias=True)
2265
+ }
2243
2266
  }
2244
2267
  if target.parameters:
2245
2268
  for param in target.parameters:
@@ -71,14 +71,14 @@ class HasClientGlobalConfig(Protocol):
71
71
  max_retries: int | None
72
72
  timeout: int | None
73
73
 
74
- def dict(self) -> dict[str, int | None]: ...
74
+ def model_dump(self) -> dict[str, int | None]: ...
75
75
 
76
76
 
77
77
  class HasClientMethodConfig(Protocol):
78
78
  name: str
79
79
  args: Any
80
80
 
81
- def dict(self) -> dict[str, str]: ...
81
+ def model_dump(self) -> dict[str, str]: ...
82
82
 
83
83
 
84
84
  class HasClientConfig(Protocol):
reconcile/utils/sloth.py CHANGED
@@ -1,5 +1,9 @@
1
+ import subprocess
2
+ import tempfile
1
3
  from io import StringIO
2
- from typing import NotRequired, TypedDict
4
+ from typing import Any, NotRequired, TypedDict
5
+
6
+ import yaml
3
7
 
4
8
  from reconcile.utils.ruamel import create_ruamel_instance
5
9
 
@@ -21,6 +25,44 @@ class PrometheusRuleSpec(TypedDict):
21
25
  groups: list[PrometheusRuleGroup]
22
26
 
23
27
 
28
+ class SLOParametersDict(TypedDict):
29
+ window: str
30
+
31
+
32
+ class SLO(TypedDict):
33
+ name: str
34
+ SLIType: str
35
+ SLISpecification: str
36
+ SLOTarget: float
37
+ SLOTargetUnit: str
38
+ SLOParameters: SLOParametersDict
39
+ SLODetails: str
40
+ dashboard: str
41
+ expr: str
42
+ SLIErrorQuery: NotRequired[str]
43
+ SLITotalQuery: NotRequired[str]
44
+
45
+
46
+ class App(TypedDict):
47
+ name: str
48
+
49
+
50
+ class SLODocument(TypedDict):
51
+ name: str
52
+ app: App
53
+ slos: NotRequired[list[SLO]]
54
+
55
+
56
+ class SlothGenerateError(Exception):
57
+ def __init__(self, msg: Any):
58
+ super().__init__("sloth generate failed: " + str(msg))
59
+
60
+
61
+ class SlothInputError(Exception):
62
+ def __init__(self, msg: Any):
63
+ super().__init__("sloth input validation failed: " + str(msg))
64
+
65
+
24
66
  def process_sloth_output(output_file_path: str) -> str:
25
67
  ruamel_instance = create_ruamel_instance()
26
68
  with open(output_file_path, encoding="utf-8") as f:
@@ -49,7 +91,134 @@ def process_sloth_output(output_file_path: str) -> str:
49
91
  rule["annotations"] = annotations
50
92
  else:
51
93
  rule.pop("annotations", None)
52
-
53
94
  with StringIO() as s:
54
95
  ruamel_instance.dump(data, s)
55
96
  return s.getvalue()
97
+
98
+
99
+ def run_sloth(spec: dict[str, Any]) -> str:
100
+ with (
101
+ tempfile.NamedTemporaryFile(
102
+ encoding="utf-8", mode="w", suffix=".yml"
103
+ ) as input_file,
104
+ tempfile.NamedTemporaryFile(
105
+ encoding="utf-8", mode="w", suffix=".yml"
106
+ ) as output_file,
107
+ ):
108
+ yaml.dump(spec, input_file, allow_unicode=True)
109
+ cmd = ["sloth", "generate", "-i", input_file.name, "-o", output_file.name]
110
+ try:
111
+ subprocess.run(cmd, capture_output=True, check=True, text=True)
112
+ except subprocess.CalledProcessError as e:
113
+ error_msg = f"{e}"
114
+ if e.stdout:
115
+ error_msg += f"\nstdout: {e.stdout}"
116
+ if e.stderr:
117
+ error_msg += f"\nstderr: {e.stderr}"
118
+ raise SlothGenerateError(error_msg) from e
119
+ return process_sloth_output(output_file.name)
120
+
121
+
122
+ def get_slo_target(slo: SLO) -> float:
123
+ """
124
+ Ensure SLO target unit aligns with format expected by sloth for 'Objective' attribute
125
+ https://pkg.go.dev/github.com/slok/sloth/pkg/prometheus/api/v1#section-readme
126
+ """
127
+ val = float(slo["SLOTarget"])
128
+ return val * (100.0 if slo.get("SLOTargetUnit") == "percent_0_1" else 1.0)
129
+
130
+
131
+ def generate_sloth_rules(
132
+ slo_document: SLODocument,
133
+ version: str = "prometheus/v1",
134
+ ) -> str:
135
+ """Generate Prometheus rules for an slo_document_v1 using sloth
136
+
137
+ Args:
138
+ slo_document query:
139
+ {
140
+ slo_docs: slo_document_v1(filter: {name: "foo"}) {
141
+ name
142
+ app {
143
+ name
144
+ }
145
+ slos {
146
+ name
147
+ SLIType
148
+ SLOTargetUnit
149
+ SLOParameters {
150
+ window
151
+ }
152
+ expr
153
+ SLOTarget
154
+ SLIErrorQuery
155
+ SLITotalQuery
156
+ SLODetails
157
+ dashboard
158
+ }
159
+ }
160
+ }
161
+ version: Spec version (default: "prometheus/v1")
162
+
163
+ Returns:
164
+ Generated Prometheus rules as YAML string
165
+ """
166
+ if not slo_document.get("slos"):
167
+ raise SlothInputError("SLO document has no SLOs defined")
168
+
169
+ service = slo_document["app"]["name"]
170
+ # only process SLOs that have both error and total queries defined
171
+ slo_input = [
172
+ {
173
+ "name": slo["name"],
174
+ "objective": get_slo_target(slo),
175
+ "description": f"{slo['name']} SLO for {service}",
176
+ "sli": {
177
+ "events": {
178
+ "error_query": slo["SLIErrorQuery"].replace(
179
+ "{{window}}", "{{.window}}"
180
+ ),
181
+ "total_query": slo["SLITotalQuery"].replace(
182
+ "{{window}}", "{{.window}}"
183
+ ),
184
+ }
185
+ },
186
+ "alerting": {
187
+ "name": f"{service.title()}{slo['name'].title()}",
188
+ "annotations": {
189
+ "summary": f"High error rate on {service} {slo['name']}",
190
+ "message": f"High error rate on {service} {slo['name']}",
191
+ "runbook": slo["SLODetails"],
192
+ "dashboard": slo["dashboard"],
193
+ },
194
+ "page_alert": {
195
+ "labels": {
196
+ "severity": "critical",
197
+ "service": service,
198
+ "slo": slo["name"],
199
+ }
200
+ },
201
+ "ticket_alert": {
202
+ "labels": {
203
+ "severity": "high",
204
+ "service": service,
205
+ "slo": slo["name"],
206
+ }
207
+ },
208
+ },
209
+ }
210
+ for slo in slo_document["slos"]
211
+ if slo.get("SLIErrorQuery") and slo.get("SLITotalQuery")
212
+ ]
213
+
214
+ if not slo_input:
215
+ raise SlothInputError(
216
+ "No SLOs found with both SLIErrorQuery and SLITotalQuery defined"
217
+ )
218
+
219
+ spec = {
220
+ "version": version,
221
+ "service": service,
222
+ "slos": slo_input,
223
+ }
224
+ return run_sloth(spec)
@@ -1,4 +1,4 @@
1
- from pydantic.dataclasses import dataclass
1
+ from dataclasses import dataclass
2
2
 
3
3
 
4
4
  @dataclass
@@ -36,6 +36,7 @@ from reconcile.typed_queries.app_interface_custom_messages import (
36
36
  )
37
37
  from reconcile.utils.aws_api import AWSApi
38
38
  from reconcile.utils.aws_helper import get_region_from_availability_zone
39
+ from reconcile.utils.datetime_util import ensure_utc, utc_now
39
40
  from reconcile.utils.external_resource_spec import (
40
41
  ExternalResourceSpec,
41
42
  ExternalResourceSpecInventory,
@@ -222,7 +223,7 @@ class TerraformClient:
222
223
  if disable_deletions_detected:
223
224
  raise RuntimeError("Terraform plan has disabled deletions detected")
224
225
 
225
- @retry(no_retry_exceptions=RdsUpgradeValidationError)
226
+ @retry(no_retry_exceptions=(RdsUpgradeValidationError,))
226
227
  def terraform_plan(
227
228
  self, spec: TerraformSpec, enable_deletion: bool
228
229
  ) -> tuple[bool, list[AccountUser], bool]:
@@ -419,11 +420,11 @@ class TerraformClient:
419
420
  deletion_approvals = account.get("deletionApprovals")
420
421
  if not deletion_approvals:
421
422
  return False
422
- now = datetime.utcnow()
423
+ now = utc_now()
423
424
  for da in deletion_approvals:
424
425
  try:
425
- expiration = datetime.strptime(
426
- da["expiration"], DATE_FORMAT
426
+ expiration = ensure_utc(
427
+ datetime.strptime(da["expiration"], DATE_FORMAT) # noqa: DTZ007
427
428
  ) + timedelta(days=1)
428
429
  except ValueError:
429
430
  raise DeletionApprovalExpirationValueError(