qontract-reconcile 0.9.1rc298__py3-none-any.whl → 0.10.1.dev1203__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 (843) hide show
  1. qontract_reconcile-0.10.1.dev1203.dist-info/METADATA +500 -0
  2. qontract_reconcile-0.10.1.dev1203.dist-info/RECORD +771 -0
  3. {qontract_reconcile-0.9.1rc298.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/WHEEL +1 -2
  4. {qontract_reconcile-0.9.1rc298.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/entry_points.txt +4 -2
  5. reconcile/acs_notifiers.py +126 -0
  6. reconcile/acs_policies.py +243 -0
  7. reconcile/acs_rbac.py +596 -0
  8. reconcile/aus/advanced_upgrade_service.py +621 -8
  9. reconcile/aus/aus_label_source.py +115 -0
  10. reconcile/aus/base.py +1053 -353
  11. reconcile/{utils → aus}/cluster_version_data.py +27 -12
  12. reconcile/aus/healthchecks.py +77 -0
  13. reconcile/aus/metrics.py +158 -0
  14. reconcile/aus/models.py +245 -5
  15. reconcile/aus/node_pool_spec.py +35 -0
  16. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +225 -110
  17. reconcile/aus/ocm_upgrade_scheduler.py +76 -71
  18. reconcile/aus/ocm_upgrade_scheduler_org.py +81 -23
  19. reconcile/aus/version_gate_approver.py +204 -0
  20. reconcile/aus/version_gates/__init__.py +12 -0
  21. reconcile/aus/version_gates/handler.py +33 -0
  22. reconcile/aus/version_gates/ingress_gate_handler.py +32 -0
  23. reconcile/aus/version_gates/ocp_gate_handler.py +26 -0
  24. reconcile/aus/version_gates/sts_version_gate_handler.py +100 -0
  25. reconcile/aws_account_manager/README.md +5 -0
  26. reconcile/aws_account_manager/integration.py +373 -0
  27. reconcile/aws_account_manager/merge_request_manager.py +114 -0
  28. reconcile/aws_account_manager/metrics.py +39 -0
  29. reconcile/aws_account_manager/reconciler.py +403 -0
  30. reconcile/aws_account_manager/utils.py +41 -0
  31. reconcile/aws_ami_cleanup/integration.py +273 -0
  32. reconcile/aws_ami_share.py +18 -14
  33. reconcile/aws_cloudwatch_log_retention/integration.py +253 -0
  34. reconcile/aws_iam_keys.py +1 -1
  35. reconcile/aws_iam_password_reset.py +56 -20
  36. reconcile/aws_saml_idp/integration.py +204 -0
  37. reconcile/aws_saml_roles/integration.py +322 -0
  38. reconcile/aws_support_cases_sos.py +2 -2
  39. reconcile/aws_version_sync/integration.py +430 -0
  40. reconcile/aws_version_sync/merge_request_manager/merge_request.py +156 -0
  41. reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py +160 -0
  42. reconcile/aws_version_sync/utils.py +64 -0
  43. reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
  44. reconcile/change_owners/README.md +34 -0
  45. reconcile/change_owners/approver.py +7 -9
  46. reconcile/change_owners/bundle.py +134 -9
  47. reconcile/change_owners/change_log_tracking.py +236 -0
  48. reconcile/change_owners/change_owners.py +204 -194
  49. reconcile/change_owners/change_types.py +183 -265
  50. reconcile/change_owners/changes.py +488 -0
  51. reconcile/change_owners/decision.py +120 -41
  52. reconcile/change_owners/diff.py +63 -92
  53. reconcile/change_owners/implicit_ownership.py +19 -16
  54. reconcile/change_owners/self_service_roles.py +158 -35
  55. reconcile/change_owners/tester.py +20 -18
  56. reconcile/checkpoint.py +4 -6
  57. reconcile/cli.py +1523 -242
  58. reconcile/closedbox_endpoint_monitoring_base.py +10 -17
  59. reconcile/cluster_auth_rhidp/integration.py +257 -0
  60. reconcile/cluster_deployment_mapper.py +2 -5
  61. reconcile/cna/assets/asset.py +4 -7
  62. reconcile/cna/assets/null.py +2 -5
  63. reconcile/cna/integration.py +2 -3
  64. reconcile/cna/state.py +6 -9
  65. reconcile/dashdotdb_base.py +31 -10
  66. reconcile/dashdotdb_cso.py +3 -6
  67. reconcile/dashdotdb_dora.py +530 -0
  68. reconcile/dashdotdb_dvo.py +10 -13
  69. reconcile/dashdotdb_slo.py +75 -19
  70. reconcile/database_access_manager.py +753 -0
  71. reconcile/deadmanssnitch.py +207 -0
  72. reconcile/dynatrace_token_provider/dependencies.py +69 -0
  73. reconcile/dynatrace_token_provider/integration.py +656 -0
  74. reconcile/dynatrace_token_provider/metrics.py +62 -0
  75. reconcile/dynatrace_token_provider/model.py +14 -0
  76. reconcile/dynatrace_token_provider/ocm.py +140 -0
  77. reconcile/dynatrace_token_provider/validate.py +48 -0
  78. reconcile/endpoints_discovery/integration.py +348 -0
  79. reconcile/endpoints_discovery/merge_request.py +96 -0
  80. reconcile/endpoints_discovery/merge_request_manager.py +178 -0
  81. reconcile/external_resources/aws.py +204 -0
  82. reconcile/external_resources/factories.py +163 -0
  83. reconcile/external_resources/integration.py +194 -0
  84. reconcile/external_resources/integration_secrets_sync.py +47 -0
  85. reconcile/external_resources/manager.py +405 -0
  86. reconcile/external_resources/meta.py +17 -0
  87. reconcile/external_resources/metrics.py +95 -0
  88. reconcile/external_resources/model.py +350 -0
  89. reconcile/external_resources/reconciler.py +265 -0
  90. reconcile/external_resources/secrets_sync.py +465 -0
  91. reconcile/external_resources/state.py +258 -0
  92. reconcile/gabi_authorized_users.py +19 -11
  93. reconcile/gcr_mirror.py +43 -34
  94. reconcile/github_org.py +4 -6
  95. reconcile/github_owners.py +1 -1
  96. reconcile/github_repo_invites.py +2 -5
  97. reconcile/gitlab_fork_compliance.py +14 -13
  98. reconcile/gitlab_housekeeping.py +185 -91
  99. reconcile/gitlab_labeler.py +15 -14
  100. reconcile/gitlab_members.py +126 -120
  101. reconcile/gitlab_owners.py +53 -66
  102. reconcile/gitlab_permissions.py +167 -6
  103. reconcile/glitchtip/README.md +150 -0
  104. reconcile/glitchtip/integration.py +99 -51
  105. reconcile/glitchtip/reconciler.py +99 -70
  106. reconcile/glitchtip_project_alerts/__init__.py +0 -0
  107. reconcile/glitchtip_project_alerts/integration.py +333 -0
  108. reconcile/glitchtip_project_dsn/integration.py +43 -43
  109. reconcile/gql_definitions/acs/__init__.py +0 -0
  110. reconcile/gql_definitions/acs/acs_instances.py +83 -0
  111. reconcile/gql_definitions/acs/acs_policies.py +239 -0
  112. reconcile/gql_definitions/acs/acs_rbac.py +111 -0
  113. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +46 -8
  114. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +38 -8
  115. reconcile/gql_definitions/app_interface_metrics_exporter/__init__.py +0 -0
  116. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +61 -0
  117. reconcile/gql_definitions/aws_account_manager/__init__.py +0 -0
  118. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +177 -0
  119. reconcile/gql_definitions/aws_ami_cleanup/__init__.py +0 -0
  120. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +161 -0
  121. reconcile/gql_definitions/aws_saml_idp/__init__.py +0 -0
  122. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +117 -0
  123. reconcile/gql_definitions/aws_saml_roles/__init__.py +0 -0
  124. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +117 -0
  125. reconcile/gql_definitions/aws_saml_roles/roles.py +97 -0
  126. reconcile/gql_definitions/aws_version_sync/__init__.py +0 -0
  127. reconcile/gql_definitions/aws_version_sync/clusters.py +83 -0
  128. reconcile/gql_definitions/aws_version_sync/namespaces.py +143 -0
  129. reconcile/gql_definitions/change_owners/queries/change_types.py +16 -29
  130. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +45 -11
  131. reconcile/gql_definitions/cluster_auth_rhidp/__init__.py +0 -0
  132. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +128 -0
  133. reconcile/gql_definitions/cna/queries/cna_provisioners.py +6 -8
  134. reconcile/gql_definitions/cna/queries/cna_resources.py +3 -5
  135. reconcile/gql_definitions/common/alerting_services_settings.py +2 -2
  136. reconcile/gql_definitions/common/app_code_component_repos.py +9 -5
  137. reconcile/gql_definitions/{glitchtip/glitchtip_settings.py → common/app_interface_custom_messages.py} +14 -16
  138. reconcile/gql_definitions/common/app_interface_dms_settings.py +86 -0
  139. reconcile/gql_definitions/common/app_interface_repo_settings.py +2 -2
  140. reconcile/gql_definitions/common/app_interface_state_settings.py +3 -5
  141. reconcile/gql_definitions/common/app_interface_vault_settings.py +3 -5
  142. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +120 -0
  143. reconcile/gql_definitions/common/apps.py +72 -0
  144. reconcile/gql_definitions/common/aws_vpc_requests.py +109 -0
  145. reconcile/gql_definitions/common/aws_vpcs.py +84 -0
  146. reconcile/gql_definitions/common/clusters.py +120 -254
  147. reconcile/gql_definitions/common/clusters_minimal.py +11 -35
  148. reconcile/gql_definitions/common/clusters_with_dms.py +72 -0
  149. reconcile/gql_definitions/common/clusters_with_peering.py +70 -98
  150. reconcile/gql_definitions/common/github_orgs.py +2 -2
  151. reconcile/gql_definitions/common/jira_settings.py +68 -0
  152. reconcile/gql_definitions/common/jiralert_settings.py +68 -0
  153. reconcile/gql_definitions/common/namespaces.py +74 -32
  154. reconcile/gql_definitions/common/namespaces_minimal.py +4 -10
  155. reconcile/gql_definitions/common/ocm_env_telemeter.py +95 -0
  156. reconcile/gql_definitions/common/ocm_environments.py +4 -2
  157. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  158. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -11
  159. reconcile/gql_definitions/common/pipeline_providers.py +45 -90
  160. reconcile/gql_definitions/common/quay_instances.py +64 -0
  161. reconcile/gql_definitions/common/quay_orgs.py +68 -0
  162. reconcile/gql_definitions/common/reserved_networks.py +94 -0
  163. reconcile/gql_definitions/common/saas_files.py +133 -95
  164. reconcile/gql_definitions/common/saas_target_namespaces.py +41 -26
  165. reconcile/gql_definitions/common/saasherder_settings.py +2 -2
  166. reconcile/gql_definitions/common/slack_workspaces.py +62 -0
  167. reconcile/gql_definitions/common/smtp_client_settings.py +2 -2
  168. reconcile/gql_definitions/common/state_aws_account.py +77 -0
  169. reconcile/gql_definitions/common/users.py +3 -2
  170. reconcile/gql_definitions/cost_report/__init__.py +0 -0
  171. reconcile/gql_definitions/cost_report/app_names.py +68 -0
  172. reconcile/gql_definitions/cost_report/cost_namespaces.py +86 -0
  173. reconcile/gql_definitions/cost_report/settings.py +77 -0
  174. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +42 -12
  175. reconcile/gql_definitions/dynatrace_token_provider/__init__.py +0 -0
  176. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +79 -0
  177. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +84 -0
  178. reconcile/gql_definitions/endpoints_discovery/__init__.py +0 -0
  179. reconcile/gql_definitions/endpoints_discovery/namespaces.py +127 -0
  180. reconcile/gql_definitions/external_resources/__init__.py +0 -0
  181. reconcile/gql_definitions/external_resources/aws_accounts.py +73 -0
  182. reconcile/gql_definitions/external_resources/external_resources_modules.py +78 -0
  183. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +1111 -0
  184. reconcile/gql_definitions/external_resources/external_resources_settings.py +98 -0
  185. reconcile/gql_definitions/fragments/aus_organization.py +34 -39
  186. reconcile/gql_definitions/fragments/aws_account_common.py +62 -0
  187. reconcile/gql_definitions/fragments/aws_account_managed.py +57 -0
  188. reconcile/gql_definitions/fragments/aws_account_sso.py +35 -0
  189. reconcile/gql_definitions/fragments/aws_infra_management_account.py +2 -2
  190. reconcile/gql_definitions/fragments/aws_vpc.py +47 -0
  191. reconcile/gql_definitions/fragments/aws_vpc_request.py +65 -0
  192. reconcile/gql_definitions/fragments/aws_vpc_request_subnet.py +29 -0
  193. reconcile/gql_definitions/fragments/deplopy_resources.py +7 -7
  194. reconcile/gql_definitions/fragments/disable.py +28 -0
  195. reconcile/gql_definitions/fragments/jumphost_common_fields.py +2 -2
  196. reconcile/gql_definitions/fragments/membership_source.py +47 -0
  197. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +29 -0
  198. reconcile/gql_definitions/fragments/oc_connection_cluster.py +4 -9
  199. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  200. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +30 -0
  201. reconcile/gql_definitions/fragments/prometheus_instance.py +48 -0
  202. reconcile/gql_definitions/fragments/resource_limits_requirements.py +29 -0
  203. reconcile/gql_definitions/fragments/{resource_requirements.py → resource_requests_requirements.py} +3 -3
  204. reconcile/gql_definitions/fragments/resource_values.py +2 -2
  205. reconcile/gql_definitions/fragments/saas_target_namespace.py +55 -12
  206. reconcile/gql_definitions/fragments/serviceaccount_token.py +38 -0
  207. reconcile/gql_definitions/fragments/terraform_state.py +36 -0
  208. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -3
  209. reconcile/gql_definitions/fragments/user.py +3 -2
  210. reconcile/gql_definitions/fragments/vault_secret.py +2 -2
  211. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +6 -2
  212. reconcile/gql_definitions/gitlab_members/permissions.py +3 -5
  213. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +16 -2
  214. reconcile/gql_definitions/glitchtip/glitchtip_project.py +22 -23
  215. reconcile/gql_definitions/glitchtip_project_alerts/__init__.py +0 -0
  216. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +173 -0
  217. reconcile/gql_definitions/integrations/integrations.py +62 -45
  218. reconcile/gql_definitions/introspection.json +51176 -0
  219. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +13 -5
  220. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +79 -0
  221. reconcile/gql_definitions/jira/__init__.py +0 -0
  222. reconcile/gql_definitions/jira/jira_servers.py +80 -0
  223. reconcile/gql_definitions/jira_permissions_validator/__init__.py +0 -0
  224. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +131 -0
  225. reconcile/gql_definitions/jumphosts/jumphosts.py +3 -5
  226. reconcile/gql_definitions/ldap_groups/__init__.py +0 -0
  227. reconcile/gql_definitions/ldap_groups/roles.py +111 -0
  228. reconcile/gql_definitions/ldap_groups/settings.py +79 -0
  229. reconcile/gql_definitions/maintenance/__init__.py +0 -0
  230. reconcile/gql_definitions/maintenance/maintenances.py +101 -0
  231. reconcile/gql_definitions/membershipsources/__init__.py +0 -0
  232. reconcile/gql_definitions/membershipsources/roles.py +112 -0
  233. reconcile/gql_definitions/ocm_labels/__init__.py +0 -0
  234. reconcile/gql_definitions/ocm_labels/clusters.py +112 -0
  235. reconcile/gql_definitions/ocm_labels/organizations.py +78 -0
  236. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  237. reconcile/gql_definitions/openshift_cluster_bots/__init__.py +0 -0
  238. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +126 -0
  239. reconcile/gql_definitions/openshift_groups/managed_groups.py +2 -2
  240. reconcile/gql_definitions/openshift_groups/managed_roles.py +3 -2
  241. reconcile/gql_definitions/openshift_serviceaccount_tokens/__init__.py +0 -0
  242. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +132 -0
  243. reconcile/gql_definitions/quay_membership/quay_membership.py +3 -5
  244. reconcile/gql_definitions/rhidp/__init__.py +0 -0
  245. reconcile/gql_definitions/rhidp/organizations.py +96 -0
  246. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +2 -2
  247. reconcile/gql_definitions/service_dependencies/service_dependencies.py +9 -31
  248. reconcile/gql_definitions/sharding/aws_accounts.py +2 -2
  249. reconcile/gql_definitions/sharding/ocm_organization.py +63 -0
  250. reconcile/gql_definitions/skupper_network/site_controller_template.py +2 -2
  251. reconcile/gql_definitions/skupper_network/skupper_networks.py +12 -38
  252. reconcile/gql_definitions/slack_usergroups/clusters.py +2 -2
  253. reconcile/gql_definitions/slack_usergroups/permissions.py +8 -15
  254. reconcile/gql_definitions/slack_usergroups/users.py +3 -2
  255. reconcile/gql_definitions/slo_documents/__init__.py +0 -0
  256. reconcile/gql_definitions/slo_documents/slo_documents.py +142 -0
  257. reconcile/gql_definitions/status_board/__init__.py +0 -0
  258. reconcile/gql_definitions/status_board/status_board.py +163 -0
  259. reconcile/gql_definitions/statuspage/statuspages.py +56 -7
  260. reconcile/gql_definitions/templating/__init__.py +0 -0
  261. reconcile/gql_definitions/templating/template_collection.py +130 -0
  262. reconcile/gql_definitions/templating/templates.py +108 -0
  263. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +4 -8
  264. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +8 -8
  265. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +6 -8
  266. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +45 -56
  267. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +4 -8
  268. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +4 -8
  269. reconcile/gql_definitions/terraform_init/__init__.py +0 -0
  270. reconcile/gql_definitions/terraform_init/aws_accounts.py +93 -0
  271. reconcile/gql_definitions/terraform_repo/__init__.py +0 -0
  272. reconcile/gql_definitions/terraform_repo/terraform_repo.py +141 -0
  273. reconcile/gql_definitions/terraform_resources/database_access_manager.py +158 -0
  274. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +153 -162
  275. reconcile/gql_definitions/terraform_tgw_attachments/__init__.py +0 -0
  276. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +119 -0
  277. reconcile/gql_definitions/unleash_feature_toggles/__init__.py +0 -0
  278. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +113 -0
  279. reconcile/gql_definitions/vault_instances/vault_instances.py +17 -50
  280. reconcile/gql_definitions/vault_policies/vault_policies.py +2 -2
  281. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +49 -12
  282. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +7 -2
  283. reconcile/integrations_manager.py +25 -13
  284. reconcile/jenkins/types.py +5 -1
  285. reconcile/jenkins_base.py +36 -0
  286. reconcile/jenkins_job_builder.py +10 -48
  287. reconcile/jenkins_job_builds_cleaner.py +40 -25
  288. reconcile/jenkins_job_cleaner.py +1 -3
  289. reconcile/jenkins_roles.py +22 -26
  290. reconcile/jenkins_webhooks.py +9 -6
  291. reconcile/jenkins_worker_fleets.py +11 -6
  292. reconcile/jira_permissions_validator.py +340 -0
  293. reconcile/jira_watcher.py +3 -5
  294. reconcile/ldap_groups/__init__.py +0 -0
  295. reconcile/ldap_groups/integration.py +279 -0
  296. reconcile/ldap_users.py +3 -0
  297. reconcile/ocm/types.py +39 -59
  298. reconcile/ocm_additional_routers.py +0 -1
  299. reconcile/ocm_addons_upgrade_tests_trigger.py +10 -15
  300. reconcile/ocm_aws_infrastructure_access.py +30 -32
  301. reconcile/ocm_clusters.py +217 -130
  302. reconcile/ocm_external_configuration_labels.py +15 -0
  303. reconcile/ocm_github_idp.py +1 -1
  304. reconcile/ocm_groups.py +25 -5
  305. reconcile/ocm_internal_notifications/__init__.py +0 -0
  306. reconcile/ocm_internal_notifications/integration.py +119 -0
  307. reconcile/ocm_labels/__init__.py +0 -0
  308. reconcile/ocm_labels/integration.py +409 -0
  309. reconcile/ocm_machine_pools.py +517 -108
  310. reconcile/ocm_upgrade_scheduler_org_updater.py +15 -11
  311. reconcile/openshift_base.py +609 -207
  312. reconcile/openshift_cluster_bots.py +344 -0
  313. reconcile/openshift_clusterrolebindings.py +15 -15
  314. reconcile/openshift_groups.py +42 -45
  315. reconcile/openshift_limitranges.py +1 -0
  316. reconcile/openshift_namespace_labels.py +22 -28
  317. reconcile/openshift_namespaces.py +22 -22
  318. reconcile/openshift_network_policies.py +4 -8
  319. reconcile/openshift_prometheus_rules.py +43 -0
  320. reconcile/openshift_resourcequotas.py +2 -16
  321. reconcile/openshift_resources.py +12 -10
  322. reconcile/openshift_resources_base.py +304 -328
  323. reconcile/openshift_rolebindings.py +18 -20
  324. reconcile/openshift_saas_deploy.py +105 -21
  325. reconcile/openshift_saas_deploy_change_tester.py +30 -35
  326. reconcile/openshift_saas_deploy_trigger_base.py +39 -36
  327. reconcile/openshift_saas_deploy_trigger_cleaner.py +41 -27
  328. reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
  329. reconcile/openshift_saas_deploy_trigger_images.py +1 -2
  330. reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
  331. reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
  332. reconcile/openshift_serviceaccount_tokens.py +138 -74
  333. reconcile/openshift_tekton_resources.py +89 -24
  334. reconcile/openshift_upgrade_watcher.py +110 -62
  335. reconcile/openshift_users.py +16 -15
  336. reconcile/openshift_vault_secrets.py +11 -6
  337. reconcile/oum/__init__.py +0 -0
  338. reconcile/oum/base.py +387 -0
  339. reconcile/oum/labelset.py +55 -0
  340. reconcile/oum/metrics.py +71 -0
  341. reconcile/oum/models.py +69 -0
  342. reconcile/oum/providers.py +59 -0
  343. reconcile/oum/standalone.py +196 -0
  344. reconcile/prometheus_rules_tester/integration.py +31 -23
  345. reconcile/quay_base.py +4 -1
  346. reconcile/quay_membership.py +1 -2
  347. reconcile/quay_mirror.py +111 -61
  348. reconcile/quay_mirror_org.py +34 -21
  349. reconcile/quay_permissions.py +7 -3
  350. reconcile/quay_repos.py +24 -32
  351. reconcile/queries.py +263 -198
  352. reconcile/query_validator.py +3 -5
  353. reconcile/resource_scraper.py +3 -4
  354. reconcile/{template_tester.py → resource_template_tester.py} +3 -3
  355. reconcile/rhidp/__init__.py +0 -0
  356. reconcile/rhidp/common.py +214 -0
  357. reconcile/rhidp/metrics.py +20 -0
  358. reconcile/rhidp/ocm_oidc_idp/__init__.py +0 -0
  359. reconcile/rhidp/ocm_oidc_idp/base.py +221 -0
  360. reconcile/rhidp/ocm_oidc_idp/integration.py +56 -0
  361. reconcile/rhidp/ocm_oidc_idp/metrics.py +22 -0
  362. reconcile/rhidp/sso_client/__init__.py +0 -0
  363. reconcile/rhidp/sso_client/base.py +266 -0
  364. reconcile/rhidp/sso_client/integration.py +60 -0
  365. reconcile/rhidp/sso_client/metrics.py +39 -0
  366. reconcile/run_integration.py +293 -0
  367. reconcile/saas_auto_promotions_manager/integration.py +69 -24
  368. reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +208 -0
  369. reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py +28 -0
  370. reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py +3 -4
  371. reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py +172 -0
  372. reconcile/saas_auto_promotions_manager/merge_request_manager/metrics.py +42 -0
  373. reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py +226 -0
  374. reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py +23 -0
  375. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +108 -32
  376. reconcile/saas_auto_promotions_manager/meta.py +4 -0
  377. reconcile/saas_auto_promotions_manager/publisher.py +32 -4
  378. reconcile/saas_auto_promotions_manager/s3_exporter.py +77 -0
  379. reconcile/saas_auto_promotions_manager/subscriber.py +110 -23
  380. reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +48 -41
  381. reconcile/saas_file_validator.py +16 -6
  382. reconcile/sendgrid_teammates.py +27 -12
  383. reconcile/service_dependencies.py +0 -3
  384. reconcile/signalfx_endpoint_monitoring.py +2 -5
  385. reconcile/skupper_network/integration.py +10 -11
  386. reconcile/skupper_network/models.py +3 -5
  387. reconcile/skupper_network/reconciler.py +28 -35
  388. reconcile/skupper_network/site_controller.py +8 -8
  389. reconcile/slack_base.py +4 -7
  390. reconcile/slack_usergroups.py +249 -171
  391. reconcile/sql_query.py +324 -171
  392. reconcile/status.py +0 -1
  393. reconcile/status_board.py +275 -0
  394. reconcile/statuspage/__init__.py +0 -5
  395. reconcile/statuspage/atlassian.py +219 -80
  396. reconcile/statuspage/integration.py +9 -97
  397. reconcile/statuspage/integrations/__init__.py +0 -0
  398. reconcile/statuspage/integrations/components.py +77 -0
  399. reconcile/statuspage/integrations/maintenances.py +111 -0
  400. reconcile/statuspage/page.py +107 -72
  401. reconcile/statuspage/state.py +6 -11
  402. reconcile/statuspage/status.py +8 -12
  403. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +60 -0
  404. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +61 -0
  405. reconcile/templating/__init__.py +0 -0
  406. reconcile/templating/lib/__init__.py +0 -0
  407. reconcile/templating/lib/merge_request_manager.py +180 -0
  408. reconcile/templating/lib/model.py +20 -0
  409. reconcile/templating/lib/rendering.py +191 -0
  410. reconcile/templating/renderer.py +410 -0
  411. reconcile/templating/validator.py +153 -0
  412. reconcile/terraform_aws_route53.py +13 -10
  413. reconcile/terraform_cloudflare_dns.py +92 -122
  414. reconcile/terraform_cloudflare_resources.py +15 -13
  415. reconcile/terraform_cloudflare_users.py +27 -27
  416. reconcile/terraform_init/__init__.py +0 -0
  417. reconcile/terraform_init/integration.py +165 -0
  418. reconcile/terraform_init/merge_request.py +57 -0
  419. reconcile/terraform_init/merge_request_manager.py +102 -0
  420. reconcile/terraform_repo.py +403 -0
  421. reconcile/terraform_resources.py +266 -168
  422. reconcile/terraform_tgw_attachments.py +417 -167
  423. reconcile/terraform_users.py +40 -17
  424. reconcile/terraform_vpc_peerings.py +310 -142
  425. reconcile/terraform_vpc_resources/__init__.py +0 -0
  426. reconcile/terraform_vpc_resources/integration.py +220 -0
  427. reconcile/terraform_vpc_resources/merge_request.py +57 -0
  428. reconcile/terraform_vpc_resources/merge_request_manager.py +107 -0
  429. reconcile/typed_queries/alerting_services_settings.py +1 -2
  430. reconcile/typed_queries/app_interface_custom_messages.py +24 -0
  431. reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +17 -0
  432. reconcile/typed_queries/app_interface_metrics_exporter/__init__.py +0 -0
  433. reconcile/typed_queries/app_interface_metrics_exporter/onboarding_status.py +13 -0
  434. reconcile/typed_queries/app_interface_repo_url.py +1 -2
  435. reconcile/typed_queries/app_interface_state_settings.py +1 -3
  436. reconcile/typed_queries/app_interface_vault_settings.py +1 -2
  437. reconcile/typed_queries/app_quay_repos_escalation_policies.py +14 -0
  438. reconcile/typed_queries/apps.py +11 -0
  439. reconcile/typed_queries/aws_vpc_requests.py +9 -0
  440. reconcile/typed_queries/aws_vpcs.py +12 -0
  441. reconcile/typed_queries/cloudflare.py +10 -0
  442. reconcile/typed_queries/clusters.py +7 -5
  443. reconcile/typed_queries/clusters_minimal.py +6 -5
  444. reconcile/typed_queries/clusters_with_dms.py +16 -0
  445. reconcile/typed_queries/cost_report/__init__.py +0 -0
  446. reconcile/typed_queries/cost_report/app_names.py +22 -0
  447. reconcile/typed_queries/cost_report/cost_namespaces.py +43 -0
  448. reconcile/typed_queries/cost_report/settings.py +15 -0
  449. reconcile/typed_queries/dynatrace.py +10 -0
  450. reconcile/typed_queries/dynatrace_environments.py +14 -0
  451. reconcile/typed_queries/dynatrace_token_provider_token_specs.py +14 -0
  452. reconcile/typed_queries/external_resources.py +46 -0
  453. reconcile/typed_queries/get_state_aws_account.py +20 -0
  454. reconcile/typed_queries/glitchtip.py +10 -0
  455. reconcile/typed_queries/jenkins.py +25 -0
  456. reconcile/typed_queries/jira.py +7 -0
  457. reconcile/typed_queries/jira_settings.py +16 -0
  458. reconcile/typed_queries/jiralert_settings.py +22 -0
  459. reconcile/typed_queries/ocm.py +8 -0
  460. reconcile/typed_queries/pagerduty_instances.py +2 -7
  461. reconcile/typed_queries/quay.py +23 -0
  462. reconcile/typed_queries/repos.py +20 -8
  463. reconcile/typed_queries/reserved_networks.py +12 -0
  464. reconcile/typed_queries/saas_files.py +221 -167
  465. reconcile/typed_queries/slack.py +7 -0
  466. reconcile/typed_queries/slo_documents.py +12 -0
  467. reconcile/typed_queries/status_board.py +58 -0
  468. reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
  469. reconcile/typed_queries/terraform_namespaces.py +1 -2
  470. reconcile/typed_queries/terraform_tgw_attachments/__init__.py +0 -0
  471. reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +16 -0
  472. reconcile/typed_queries/unleash.py +10 -0
  473. reconcile/typed_queries/users.py +11 -0
  474. reconcile/typed_queries/vault.py +10 -0
  475. reconcile/unleash_feature_toggles/__init__.py +0 -0
  476. reconcile/unleash_feature_toggles/integration.py +287 -0
  477. reconcile/utils/acs/__init__.py +0 -0
  478. reconcile/utils/acs/base.py +81 -0
  479. reconcile/utils/acs/notifiers.py +143 -0
  480. reconcile/utils/acs/policies.py +163 -0
  481. reconcile/utils/acs/rbac.py +277 -0
  482. reconcile/utils/aggregated_list.py +11 -9
  483. reconcile/utils/amtool.py +6 -4
  484. reconcile/utils/aws_api.py +279 -66
  485. reconcile/utils/aws_api_typed/__init__.py +0 -0
  486. reconcile/utils/aws_api_typed/account.py +23 -0
  487. reconcile/utils/aws_api_typed/api.py +273 -0
  488. reconcile/utils/aws_api_typed/dynamodb.py +16 -0
  489. reconcile/utils/aws_api_typed/iam.py +67 -0
  490. reconcile/utils/aws_api_typed/organization.py +152 -0
  491. reconcile/utils/aws_api_typed/s3.py +26 -0
  492. reconcile/utils/aws_api_typed/service_quotas.py +79 -0
  493. reconcile/utils/aws_api_typed/sts.py +36 -0
  494. reconcile/utils/aws_api_typed/support.py +79 -0
  495. reconcile/utils/aws_helper.py +42 -3
  496. reconcile/utils/batches.py +11 -0
  497. reconcile/utils/binary.py +7 -9
  498. reconcile/utils/cloud_resource_best_practice/__init__.py +0 -0
  499. reconcile/utils/cloud_resource_best_practice/aws_rds.py +66 -0
  500. reconcile/utils/clusterhealth/__init__.py +0 -0
  501. reconcile/utils/clusterhealth/providerbase.py +39 -0
  502. reconcile/utils/clusterhealth/telemeter.py +39 -0
  503. reconcile/utils/config.py +3 -4
  504. reconcile/utils/deadmanssnitch_api.py +86 -0
  505. reconcile/utils/differ.py +205 -0
  506. reconcile/utils/disabled_integrations.py +4 -6
  507. reconcile/utils/dynatrace/__init__.py +0 -0
  508. reconcile/utils/dynatrace/client.py +93 -0
  509. reconcile/utils/early_exit_cache.py +289 -0
  510. reconcile/utils/elasticsearch_exceptions.py +5 -0
  511. reconcile/utils/environ.py +2 -2
  512. reconcile/utils/exceptions.py +4 -0
  513. reconcile/utils/expiration.py +4 -8
  514. reconcile/utils/extended_early_exit.py +210 -0
  515. reconcile/utils/external_resource_spec.py +34 -12
  516. reconcile/utils/external_resources.py +48 -20
  517. reconcile/utils/filtering.py +16 -0
  518. reconcile/utils/git.py +49 -16
  519. reconcile/utils/github_api.py +10 -9
  520. reconcile/utils/gitlab_api.py +333 -190
  521. reconcile/utils/glitchtip/client.py +97 -100
  522. reconcile/utils/glitchtip/models.py +89 -11
  523. reconcile/utils/gql.py +157 -58
  524. reconcile/utils/grouping.py +17 -0
  525. reconcile/utils/helm.py +89 -18
  526. reconcile/utils/helpers.py +51 -0
  527. reconcile/utils/imap_client.py +5 -6
  528. reconcile/utils/internal_groups/__init__.py +0 -0
  529. reconcile/utils/internal_groups/client.py +160 -0
  530. reconcile/utils/internal_groups/models.py +71 -0
  531. reconcile/utils/jenkins_api.py +10 -34
  532. reconcile/utils/jinja2/__init__.py +0 -0
  533. reconcile/utils/{jinja2_ext.py → jinja2/extensions.py} +6 -4
  534. reconcile/utils/jinja2/filters.py +142 -0
  535. reconcile/utils/jinja2/utils.py +278 -0
  536. reconcile/utils/jira_client.py +165 -8
  537. reconcile/utils/jjb_client.py +47 -35
  538. reconcile/utils/jobcontroller/__init__.py +0 -0
  539. reconcile/utils/jobcontroller/controller.py +413 -0
  540. reconcile/utils/jobcontroller/models.py +195 -0
  541. reconcile/utils/jsonpath.py +4 -5
  542. reconcile/utils/jump_host.py +13 -12
  543. reconcile/utils/keycloak.py +106 -0
  544. reconcile/utils/ldap_client.py +35 -6
  545. reconcile/utils/lean_terraform_client.py +115 -6
  546. reconcile/utils/membershipsources/__init__.py +0 -0
  547. reconcile/utils/membershipsources/app_interface_resolver.py +60 -0
  548. reconcile/utils/membershipsources/models.py +91 -0
  549. reconcile/utils/membershipsources/resolver.py +110 -0
  550. reconcile/utils/merge_request_manager/__init__.py +0 -0
  551. reconcile/utils/merge_request_manager/merge_request_manager.py +99 -0
  552. reconcile/utils/merge_request_manager/parser.py +67 -0
  553. reconcile/utils/metrics.py +511 -1
  554. reconcile/utils/models.py +123 -0
  555. reconcile/utils/mr/README.md +198 -0
  556. reconcile/utils/mr/__init__.py +14 -10
  557. reconcile/utils/mr/app_interface_reporter.py +2 -2
  558. reconcile/utils/mr/aws_access.py +4 -4
  559. reconcile/utils/mr/base.py +51 -31
  560. reconcile/utils/mr/clusters_updates.py +10 -7
  561. reconcile/utils/mr/glitchtip_access_reporter.py +2 -4
  562. reconcile/utils/mr/labels.py +14 -1
  563. reconcile/utils/mr/notificator.py +1 -3
  564. reconcile/utils/mr/ocm_update_recommended_version.py +1 -2
  565. reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +7 -3
  566. reconcile/utils/mr/promote_qontract.py +203 -0
  567. reconcile/utils/mr/user_maintenance.py +24 -4
  568. reconcile/utils/oauth2_backend_application_session.py +132 -0
  569. reconcile/utils/oc.py +194 -170
  570. reconcile/utils/oc_connection_parameters.py +40 -51
  571. reconcile/utils/oc_filters.py +11 -13
  572. reconcile/utils/oc_map.py +14 -35
  573. reconcile/utils/ocm/__init__.py +30 -1
  574. reconcile/utils/ocm/addons.py +228 -0
  575. reconcile/utils/ocm/base.py +618 -5
  576. reconcile/utils/ocm/cluster_groups.py +5 -56
  577. reconcile/utils/ocm/clusters.py +111 -99
  578. reconcile/utils/ocm/identity_providers.py +66 -0
  579. reconcile/utils/ocm/label_sources.py +75 -0
  580. reconcile/utils/ocm/labels.py +139 -54
  581. reconcile/utils/ocm/manifests.py +39 -0
  582. reconcile/utils/ocm/ocm.py +182 -928
  583. reconcile/utils/ocm/products.py +758 -0
  584. reconcile/utils/ocm/search_filters.py +20 -28
  585. reconcile/utils/ocm/service_log.py +32 -79
  586. reconcile/utils/ocm/sre_capability_labels.py +51 -0
  587. reconcile/utils/ocm/status_board.py +66 -0
  588. reconcile/utils/ocm/subscriptions.py +49 -59
  589. reconcile/utils/ocm/syncsets.py +39 -0
  590. reconcile/utils/ocm/upgrades.py +181 -0
  591. reconcile/utils/ocm_base_client.py +71 -36
  592. reconcile/utils/openshift_resource.py +113 -67
  593. reconcile/utils/output.py +18 -11
  594. reconcile/utils/pagerduty_api.py +16 -10
  595. reconcile/utils/parse_dhms_duration.py +13 -1
  596. reconcile/utils/prometheus.py +123 -0
  597. reconcile/utils/promotion_state.py +56 -19
  598. reconcile/utils/promtool.py +5 -8
  599. reconcile/utils/quay_api.py +13 -25
  600. reconcile/utils/raw_github_api.py +3 -5
  601. reconcile/utils/repo_owners.py +2 -8
  602. reconcile/utils/rest_api_base.py +126 -0
  603. reconcile/utils/rosa/__init__.py +0 -0
  604. reconcile/utils/rosa/rosa_cli.py +310 -0
  605. reconcile/utils/rosa/session.py +201 -0
  606. reconcile/utils/ruamel.py +16 -0
  607. reconcile/utils/runtime/__init__.py +0 -1
  608. reconcile/utils/runtime/desired_state_diff.py +9 -20
  609. reconcile/utils/runtime/environment.py +33 -8
  610. reconcile/utils/runtime/integration.py +28 -12
  611. reconcile/utils/runtime/meta.py +1 -3
  612. reconcile/utils/runtime/runner.py +8 -11
  613. reconcile/utils/runtime/sharding.py +93 -36
  614. reconcile/utils/saasherder/__init__.py +1 -1
  615. reconcile/utils/saasherder/interfaces.py +143 -138
  616. reconcile/utils/saasherder/models.py +201 -43
  617. reconcile/utils/saasherder/saasherder.py +508 -378
  618. reconcile/utils/secret_reader.py +22 -27
  619. reconcile/utils/semver_helper.py +15 -1
  620. reconcile/utils/slack_api.py +124 -36
  621. reconcile/utils/smtp_client.py +1 -2
  622. reconcile/utils/sqs_gateway.py +10 -6
  623. reconcile/utils/state.py +276 -127
  624. reconcile/utils/terraform/config_client.py +6 -7
  625. reconcile/utils/terraform_client.py +284 -125
  626. reconcile/utils/terrascript/cloudflare_client.py +38 -17
  627. reconcile/utils/terrascript/cloudflare_resources.py +67 -18
  628. reconcile/utils/terrascript/models.py +2 -3
  629. reconcile/utils/terrascript/resources.py +1 -2
  630. reconcile/utils/terrascript_aws_client.py +1292 -540
  631. reconcile/utils/three_way_diff_strategy.py +157 -0
  632. reconcile/utils/unleash/__init__.py +11 -0
  633. reconcile/utils/{unleash.py → unleash/client.py} +35 -29
  634. reconcile/utils/unleash/server.py +145 -0
  635. reconcile/utils/vault.py +42 -32
  636. reconcile/utils/vaultsecretref.py +2 -4
  637. reconcile/utils/vcs.py +250 -0
  638. reconcile/vault_replication.py +38 -31
  639. reconcile/vpc_peerings_validator.py +82 -13
  640. tools/app_interface_metrics_exporter.py +70 -0
  641. tools/app_interface_reporter.py +44 -157
  642. tools/cli_commands/container_images_report.py +154 -0
  643. tools/cli_commands/cost_report/__init__.py +0 -0
  644. tools/cli_commands/cost_report/aws.py +137 -0
  645. tools/cli_commands/cost_report/cost_management_api.py +155 -0
  646. tools/cli_commands/cost_report/model.py +49 -0
  647. tools/cli_commands/cost_report/openshift.py +166 -0
  648. tools/cli_commands/cost_report/openshift_cost_optimization.py +187 -0
  649. tools/cli_commands/cost_report/response.py +124 -0
  650. tools/cli_commands/cost_report/util.py +72 -0
  651. tools/cli_commands/cost_report/view.py +524 -0
  652. tools/cli_commands/erv2.py +620 -0
  653. tools/cli_commands/gpg_encrypt.py +5 -8
  654. tools/cli_commands/systems_and_tools.py +489 -0
  655. tools/glitchtip_access_revalidation.py +1 -1
  656. tools/qontract_cli.py +2301 -673
  657. tools/saas_metrics_exporter/__init__.py +0 -0
  658. tools/saas_metrics_exporter/commit_distance/__init__.py +0 -0
  659. tools/saas_metrics_exporter/commit_distance/channel.py +63 -0
  660. tools/saas_metrics_exporter/commit_distance/commit_distance.py +103 -0
  661. tools/saas_metrics_exporter/commit_distance/metrics.py +19 -0
  662. tools/saas_metrics_exporter/main.py +99 -0
  663. tools/saas_promotion_state/__init__.py +0 -0
  664. tools/saas_promotion_state/saas_promotion_state.py +105 -0
  665. tools/sd_app_sre_alert_report.py +145 -0
  666. tools/template_validation.py +107 -0
  667. e2e_tests/cli.py +0 -83
  668. e2e_tests/create_namespace.py +0 -43
  669. e2e_tests/dedicated_admin_rolebindings.py +0 -44
  670. e2e_tests/dedicated_admin_test_base.py +0 -39
  671. e2e_tests/default_network_policies.py +0 -47
  672. e2e_tests/default_project_labels.py +0 -52
  673. e2e_tests/network_policy_test_base.py +0 -17
  674. e2e_tests/test_base.py +0 -56
  675. qontract_reconcile-0.9.1rc298.dist-info/METADATA +0 -63
  676. qontract_reconcile-0.9.1rc298.dist-info/RECORD +0 -585
  677. qontract_reconcile-0.9.1rc298.dist-info/top_level.txt +0 -4
  678. reconcile/ecr_mirror.py +0 -152
  679. reconcile/github_scanner.py +0 -74
  680. reconcile/gitlab_integrations.py +0 -63
  681. reconcile/gql_definitions/ocm_oidc_idp/clusters.py +0 -195
  682. reconcile/gql_definitions/ocp_release_mirror/ocp_release_mirror.py +0 -287
  683. reconcile/integrations_validator.py +0 -18
  684. reconcile/jenkins_plugins.py +0 -129
  685. reconcile/kafka_clusters.py +0 -208
  686. reconcile/ocm_cluster_admin.py +0 -42
  687. reconcile/ocm_oidc_idp.py +0 -198
  688. reconcile/ocp_release_mirror.py +0 -373
  689. reconcile/prometheus_rules_tester_old.py +0 -436
  690. reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py +0 -279
  691. reconcile/saas_auto_promotions_manager/utils/vcs.py +0 -141
  692. reconcile/sentry_config.py +0 -613
  693. reconcile/sentry_helper.py +0 -69
  694. reconcile/test/conftest.py +0 -187
  695. reconcile/test/fixtures.py +0 -24
  696. reconcile/test/saas_auto_promotions_manager/conftest.py +0 -69
  697. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -110
  698. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -10
  699. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_housekeeping.py +0 -200
  700. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -151
  701. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -63
  702. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/data_keys.py +0 -4
  703. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -46
  704. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -94
  705. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -44
  706. reconcile/test/saas_auto_promotions_manager/subscriber/conftest.py +0 -74
  707. reconcile/test/saas_auto_promotions_manager/subscriber/data_keys.py +0 -11
  708. reconcile/test/saas_auto_promotions_manager/subscriber/test_content_hash.py +0 -155
  709. reconcile/test/saas_auto_promotions_manager/subscriber/test_diff.py +0 -173
  710. reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_config_hash.py +0 -226
  711. reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_moving_ref.py +0 -224
  712. reconcile/test/saas_auto_promotions_manager/subscriber/test_single_channel_with_single_publisher.py +0 -350
  713. reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -129
  714. reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_multiple_publishers_for_single_channel.py +0 -70
  715. reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_use_target_config_hash.py +0 -63
  716. reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_with_auto_promote.py +0 -74
  717. reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_without_auto_promote.py +0 -65
  718. reconcile/test/test_aggregated_list.py +0 -237
  719. reconcile/test/test_amtool.py +0 -37
  720. reconcile/test/test_auto_promoter.py +0 -295
  721. reconcile/test/test_aws_ami_share.py +0 -68
  722. reconcile/test/test_aws_iam_keys.py +0 -70
  723. reconcile/test/test_aws_iam_password_reset.py +0 -35
  724. reconcile/test/test_aws_support_cases_sos.py +0 -23
  725. reconcile/test/test_checkpoint.py +0 -178
  726. reconcile/test/test_cli.py +0 -41
  727. reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
  728. reconcile/test/test_gabi_authorized_users.py +0 -72
  729. reconcile/test/test_github_org.py +0 -154
  730. reconcile/test/test_github_repo_invites.py +0 -123
  731. reconcile/test/test_gitlab_housekeeping.py +0 -88
  732. reconcile/test/test_gitlab_labeler.py +0 -129
  733. reconcile/test/test_gitlab_members.py +0 -283
  734. reconcile/test/test_instrumented_wrappers.py +0 -18
  735. reconcile/test/test_integrations_manager.py +0 -995
  736. reconcile/test/test_jenkins_worker_fleets.py +0 -55
  737. reconcile/test/test_jump_host.py +0 -117
  738. reconcile/test/test_ldap_users.py +0 -123
  739. reconcile/test/test_make.py +0 -28
  740. reconcile/test/test_ocm_additional_routers.py +0 -134
  741. reconcile/test/test_ocm_addons_upgrade_scheduler_org.py +0 -149
  742. reconcile/test/test_ocm_clusters.py +0 -598
  743. reconcile/test/test_ocm_clusters_manifest_updates.py +0 -89
  744. reconcile/test/test_ocm_oidc_idp.py +0 -315
  745. reconcile/test/test_ocm_update_recommended_version.py +0 -145
  746. reconcile/test/test_ocm_upgrade_scheduler.py +0 -614
  747. reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -129
  748. reconcile/test/test_openshift_base.py +0 -730
  749. reconcile/test/test_openshift_namespace_labels.py +0 -345
  750. reconcile/test/test_openshift_namespaces.py +0 -256
  751. reconcile/test/test_openshift_resource.py +0 -415
  752. reconcile/test/test_openshift_resources_base.py +0 -440
  753. reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -310
  754. reconcile/test/test_openshift_tekton_resources.py +0 -253
  755. reconcile/test/test_openshift_upgrade_watcher.py +0 -146
  756. reconcile/test/test_prometheus_rules_tester.py +0 -151
  757. reconcile/test/test_prometheus_rules_tester_old.py +0 -77
  758. reconcile/test/test_quay_membership.py +0 -86
  759. reconcile/test/test_quay_mirror.py +0 -109
  760. reconcile/test/test_quay_mirror_org.py +0 -70
  761. reconcile/test/test_quay_repos.py +0 -59
  762. reconcile/test/test_queries.py +0 -53
  763. reconcile/test/test_repo_owners.py +0 -47
  764. reconcile/test/test_requests_sender.py +0 -139
  765. reconcile/test/test_saasherder.py +0 -1074
  766. reconcile/test/test_saasherder_allowed_secret_paths.py +0 -127
  767. reconcile/test/test_secret_reader.py +0 -153
  768. reconcile/test/test_slack_base.py +0 -185
  769. reconcile/test/test_slack_usergroups.py +0 -744
  770. reconcile/test/test_sql_query.py +0 -19
  771. reconcile/test/test_terraform_cloudflare_dns.py +0 -117
  772. reconcile/test/test_terraform_cloudflare_resources.py +0 -106
  773. reconcile/test/test_terraform_cloudflare_users.py +0 -749
  774. reconcile/test/test_terraform_resources.py +0 -257
  775. reconcile/test/test_terraform_tgw_attachments.py +0 -631
  776. reconcile/test/test_terraform_users.py +0 -57
  777. reconcile/test/test_terraform_vpc_peerings.py +0 -499
  778. reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1061
  779. reconcile/test/test_unleash.py +0 -138
  780. reconcile/test/test_utils_aws_api.py +0 -240
  781. reconcile/test/test_utils_aws_helper.py +0 -80
  782. reconcile/test/test_utils_cluster_version_data.py +0 -177
  783. reconcile/test/test_utils_data_structures.py +0 -13
  784. reconcile/test/test_utils_disabled_integrations.py +0 -86
  785. reconcile/test/test_utils_expiration.py +0 -109
  786. reconcile/test/test_utils_external_resource_spec.py +0 -383
  787. reconcile/test/test_utils_external_resources.py +0 -247
  788. reconcile/test/test_utils_github_api.py +0 -73
  789. reconcile/test/test_utils_gitlab_api.py +0 -20
  790. reconcile/test/test_utils_gpg.py +0 -69
  791. reconcile/test/test_utils_gql.py +0 -81
  792. reconcile/test/test_utils_helm.py +0 -306
  793. reconcile/test/test_utils_helpers.py +0 -55
  794. reconcile/test/test_utils_imap_client.py +0 -65
  795. reconcile/test/test_utils_jjb_client.py +0 -52
  796. reconcile/test/test_utils_jsonpath.py +0 -286
  797. reconcile/test/test_utils_ldap_client.py +0 -51
  798. reconcile/test/test_utils_mr.py +0 -226
  799. reconcile/test/test_utils_mr_clusters_updates.py +0 -77
  800. reconcile/test/test_utils_oc.py +0 -984
  801. reconcile/test/test_utils_ocm.py +0 -110
  802. reconcile/test/test_utils_pagerduty_api.py +0 -251
  803. reconcile/test/test_utils_parse_dhms_duration.py +0 -34
  804. reconcile/test/test_utils_password_validator.py +0 -155
  805. reconcile/test/test_utils_quay_api.py +0 -86
  806. reconcile/test/test_utils_semver_helper.py +0 -19
  807. reconcile/test/test_utils_sharding.py +0 -56
  808. reconcile/test/test_utils_slack_api.py +0 -439
  809. reconcile/test/test_utils_smtp_client.py +0 -73
  810. reconcile/test/test_utils_state.py +0 -256
  811. reconcile/test/test_utils_terraform.py +0 -13
  812. reconcile/test/test_utils_terraform_client.py +0 -585
  813. reconcile/test/test_utils_terraform_config_client.py +0 -219
  814. reconcile/test/test_utils_terrascript_aws_client.py +0 -277
  815. reconcile/test/test_utils_terrascript_cloudflare_client.py +0 -597
  816. reconcile/test/test_utils_terrascript_cloudflare_resources.py +0 -26
  817. reconcile/test/test_vault_replication.py +0 -515
  818. reconcile/test/test_vault_utils.py +0 -47
  819. reconcile/test/test_version_bump.py +0 -18
  820. reconcile/test/test_vpc_peerings_validator.py +0 -103
  821. reconcile/test/test_wrong_region.py +0 -78
  822. reconcile/typed_queries/glitchtip_settings.py +0 -18
  823. reconcile/typed_queries/ocp_release_mirror.py +0 -11
  824. reconcile/unleash_watcher.py +0 -120
  825. reconcile/utils/git_secrets.py +0 -63
  826. reconcile/utils/mr/auto_promoter.py +0 -218
  827. reconcile/utils/sentry_client.py +0 -383
  828. release/test_version.py +0 -50
  829. release/version.py +0 -100
  830. tools/test/test_qontract_cli.py +0 -60
  831. tools/test/test_sre_checkpoints.py +0 -79
  832. /e2e_tests/__init__.py → /reconcile/aus/upgrades.py +0 -0
  833. /reconcile/{gql_definitions/ocp_release_mirror → aws_account_manager}/__init__.py +0 -0
  834. /reconcile/{test → aws_ami_cleanup}/__init__.py +0 -0
  835. /reconcile/{test/saas_auto_promotions_manager → aws_cloudwatch_log_retention}/__init__.py +0 -0
  836. /reconcile/{test/saas_auto_promotions_manager/merge_request_manager → aws_saml_idp}/__init__.py +0 -0
  837. /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager → aws_saml_roles}/__init__.py +0 -0
  838. /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/renderer → aws_version_sync}/__init__.py +0 -0
  839. /reconcile/{test/saas_auto_promotions_manager/subscriber → aws_version_sync/merge_request_manager}/__init__.py +0 -0
  840. /reconcile/{test/saas_auto_promotions_manager/utils → cluster_auth_rhidp}/__init__.py +0 -0
  841. /reconcile/{test/saas_auto_promotions_manager/utils/saas_files_inventory → dynatrace_token_provider}/__init__.py +0 -0
  842. {release → reconcile/endpoints_discovery}/__init__.py +0 -0
  843. {tools/test → reconcile/external_resources}/__init__.py +0 -0
@@ -1,5 +1,6 @@
1
1
  import itertools
2
2
  import logging
3
+ from collections import Counter
3
4
  from collections.abc import (
4
5
  Iterable,
5
6
  Mapping,
@@ -11,9 +12,7 @@ from dataclasses import (
11
12
  )
12
13
  from typing import (
13
14
  Any,
14
- Optional,
15
15
  Protocol,
16
- Union,
17
16
  runtime_checkable,
18
17
  )
19
18
 
@@ -24,6 +23,10 @@ from sretoolbox.utils import (
24
23
  )
25
24
 
26
25
  from reconcile import queries
26
+ from reconcile.utils import (
27
+ differ,
28
+ metrics,
29
+ )
27
30
  from reconcile.utils.oc import (
28
31
  DeploymentFieldIsImmutableError,
29
32
  FieldIsImmutableError,
@@ -31,16 +34,21 @@ from reconcile.utils.oc import (
31
34
  MayNotChangeOnceSetError,
32
35
  MetaDataAnnotationsTooLongApplyError,
33
36
  OC_Map,
37
+ OCCli,
34
38
  OCClient,
35
- OCDeprecated,
36
39
  OCLogMsg,
37
40
  PrimaryClusterIPCanNotBeUnsetError,
41
+ RequestEntityTooLargeError,
38
42
  StatefulSetUpdateForbidden,
39
43
  StatusCodeError,
40
44
  UnsupportedMediaTypeError,
41
45
  )
42
46
  from reconcile.utils.openshift_resource import OpenshiftResource as OR
43
- from reconcile.utils.openshift_resource import ResourceInventory
47
+ from reconcile.utils.openshift_resource import (
48
+ OpenshiftResourceInventoryGauge,
49
+ ResourceInventory,
50
+ )
51
+ from reconcile.utils.three_way_diff_strategy import three_way_diff_using_hash
44
52
 
45
53
  ACTION_APPLIED = "applied"
46
54
  ACTION_DELETED = "deleted"
@@ -64,7 +72,7 @@ class BaseStateSpec:
64
72
  @dataclass
65
73
  class CurrentStateSpec(BaseStateSpec):
66
74
  kind: str
67
- resource_names: Optional[Iterable[str]]
75
+ resource_names: Iterable[str] | None
68
76
 
69
77
 
70
78
  @dataclass
@@ -74,7 +82,7 @@ class DesiredStateSpec(BaseStateSpec):
74
82
  privileged: bool = False
75
83
 
76
84
 
77
- StateSpec = Union[CurrentStateSpec, DesiredStateSpec]
85
+ StateSpec = CurrentStateSpec | DesiredStateSpec
78
86
 
79
87
 
80
88
  @runtime_checkable
@@ -104,27 +112,23 @@ class HasOrgAndGithubUsername(Protocol):
104
112
  class ClusterMap(Protocol):
105
113
  """An OCMap protocol."""
106
114
 
107
- def get(
108
- self, cluster: str, privileged: bool = False
109
- ) -> Union[OCDeprecated, OCLogMsg]:
110
- ...
115
+ def get(self, cluster: str, privileged: bool = False) -> OCCli | OCLogMsg: ...
111
116
 
112
- def get_cluster(self, cluster: str, privileged: bool = False) -> OCDeprecated:
113
- ...
117
+ def get_cluster(self, cluster: str, privileged: bool = False) -> OCCli: ...
114
118
 
115
119
  def clusters(
116
120
  self, include_errors: bool = False, privileged: bool = False
117
- ) -> list[str]:
118
- ...
121
+ ) -> list[str]: ...
119
122
 
120
123
 
121
124
  def init_specs_to_fetch(
122
125
  ri: ResourceInventory,
123
126
  oc_map: ClusterMap,
124
- namespaces: Optional[Iterable[Mapping]] = None,
125
- clusters: Optional[Iterable[Mapping]] = None,
126
- override_managed_types: Optional[Iterable[str]] = None,
127
+ namespaces: Iterable[Mapping] | None = None,
128
+ clusters: Iterable[Mapping] | None = None,
129
+ override_managed_types: Iterable[str] | None = None,
127
130
  managed_types_key: str = "managedResourceTypes",
131
+ cluster_admin: bool = False,
128
132
  ) -> list[StateSpec]:
129
133
  state_specs: list[StateSpec] = []
130
134
 
@@ -141,7 +145,9 @@ def init_specs_to_fetch(
141
145
  continue
142
146
 
143
147
  cluster = namespace_info["cluster"]["name"]
144
- privileged = namespace_info.get("clusterAdmin", False) is True
148
+ privileged = (
149
+ namespace_info.get("clusterAdmin", False) is True or cluster_admin
150
+ )
145
151
  try:
146
152
  oc = oc_map.get_cluster(cluster, privileged)
147
153
  except OCLogMsg as ex:
@@ -199,7 +205,9 @@ def init_specs_to_fetch(
199
205
  for kind in managed_types:
200
206
  managed_names = resource_names.get(kind)
201
207
  kind_to_use = resource_type_overrides.get(kind, kind)
202
- ri.initialize_resource_type(cluster, namespace, kind_to_use)
208
+ ri.initialize_resource_type(
209
+ cluster, namespace, kind_to_use, managed_names
210
+ )
203
211
  state_specs.append(
204
212
  CurrentStateSpec(
205
213
  oc=oc,
@@ -282,7 +290,7 @@ def populate_current_state(
282
290
  ri: ResourceInventory,
283
291
  integration: str,
284
292
  integration_version: str,
285
- caller: Optional[str] = None,
293
+ caller: str | None = None,
286
294
  ):
287
295
  # if spec.oc is None: - oc can't be none because init_namespace_specs_to_fetch does not create specs if oc is none
288
296
  # return
@@ -292,7 +300,9 @@ def populate_current_state(
292
300
  return
293
301
  try:
294
302
  for item in spec.oc.get_items(
295
- spec.kind, namespace=spec.namespace, resource_names=spec.resource_names
303
+ spec.kind,
304
+ namespace=spec.namespace,
305
+ resource_names=spec.resource_names,
296
306
  ):
297
307
  openshift_resource = OR(item, integration, integration_version)
298
308
 
@@ -308,21 +318,22 @@ def populate_current_state(
308
318
  )
309
319
  except StatusCodeError as e:
310
320
  ri.register_error(cluster=spec.cluster)
311
- logging.error(f"[{spec.cluster}/{spec.namespace}] {str(e)}")
321
+ logging.error(f"[{spec.cluster}/{spec.namespace}] {e!s}")
312
322
 
313
323
 
314
324
  def fetch_current_state(
315
- namespaces: Optional[Iterable[Mapping]] = None,
316
- clusters: Optional[Iterable[Mapping]] = None,
317
- thread_pool_size: Optional[int] = None,
318
- integration: Optional[str] = None,
319
- integration_version: Optional[str] = None,
320
- override_managed_types: Optional[Iterable[str]] = None,
321
- internal: Optional[bool] = None,
325
+ namespaces: Iterable[Mapping] | None = None,
326
+ clusters: Iterable[Mapping] | None = None,
327
+ thread_pool_size: int | None = None,
328
+ integration: str | None = None,
329
+ integration_version: str | None = None,
330
+ override_managed_types: Iterable[str] | None = None,
331
+ internal: bool | None = None,
322
332
  use_jump_host: bool = True,
323
333
  init_api_resources: bool = False,
324
334
  cluster_admin: bool = False,
325
- caller: Optional[str] = None,
335
+ caller: str | None = None,
336
+ init_projects: bool = False,
326
337
  ) -> tuple[ResourceInventory, OC_Map]:
327
338
  ri = ResourceInventory()
328
339
  settings = queries.get_app_interface_settings()
@@ -336,6 +347,7 @@ def fetch_current_state(
336
347
  thread_pool_size=thread_pool_size,
337
348
  init_api_resources=init_api_resources,
338
349
  cluster_admin=cluster_admin,
350
+ init_projects=init_projects,
339
351
  )
340
352
  state_specs = init_specs_to_fetch(
341
353
  ri,
@@ -343,6 +355,7 @@ def fetch_current_state(
343
355
  namespaces=namespaces,
344
356
  clusters=clusters,
345
357
  override_managed_types=override_managed_types,
358
+ cluster_admin=cluster_admin,
346
359
  )
347
360
  threaded.run(
348
361
  populate_current_state,
@@ -374,7 +387,14 @@ def apply(
374
387
  recycle_pods: bool = True,
375
388
  privileged: bool = False,
376
389
  ) -> None:
377
- logging.info(["apply", cluster, namespace, resource_type, resource.name])
390
+ logging.info([
391
+ "apply",
392
+ f"privileged={privileged}",
393
+ cluster,
394
+ namespace,
395
+ resource_type,
396
+ resource.name,
397
+ ])
378
398
 
379
399
  try:
380
400
  oc = oc_map.get_cluster(cluster, privileged)
@@ -401,7 +421,11 @@ def apply(
401
421
  namespace, resource_type, resource.name
402
422
  )
403
423
  oc.apply(namespace, annotated)
404
- except (MetaDataAnnotationsTooLongApplyError, UnsupportedMediaTypeError):
424
+ except (
425
+ MetaDataAnnotationsTooLongApplyError,
426
+ UnsupportedMediaTypeError,
427
+ RequestEntityTooLargeError,
428
+ ):
405
429
  if not oc.get(
406
430
  namespace, resource_type, resource.name, allow_not_found=True
407
431
  ):
@@ -410,7 +434,7 @@ def apply(
410
434
  except FieldIsImmutableError:
411
435
  # Add more resources types to the list when you're
412
436
  # sure they're safe.
413
- if resource_type not in ["Route", "Service", "Secret"]:
437
+ if resource_type not in {"Route", "Service", "Secret", "Job"}:
414
438
  raise
415
439
  oc.delete(namespace=namespace, kind=resource_type, name=resource.name)
416
440
  oc.apply(namespace=namespace, resource=annotated)
@@ -464,7 +488,7 @@ def apply(
464
488
  obsolete_rs["metadata"]["ownerReferences"] = owner_references
465
489
  oc.apply(namespace=namespace, resource=OR(obsolete_rs, "", ""))
466
490
  except (MayNotChangeOnceSetError, PrimaryClusterIPCanNotBeUnsetError):
467
- if resource_type not in ["Service"]:
491
+ if resource_type not in {"Service"}:
468
492
  raise
469
493
 
470
494
  oc.delete(namespace=namespace, kind=resource_type, name=resource.name)
@@ -473,15 +497,13 @@ def apply(
473
497
  if resource_type != "StatefulSet":
474
498
  raise
475
499
 
476
- logging.info(
477
- [
478
- "delete_sts_and_apply",
479
- cluster,
480
- namespace,
481
- resource_type,
482
- resource.name,
483
- ]
484
- )
500
+ logging.info([
501
+ "delete_sts_and_apply",
502
+ cluster,
503
+ namespace,
504
+ resource_type,
505
+ resource.name,
506
+ ])
485
507
  current_resource = oc.get(namespace, resource_type, resource.name)
486
508
  current_storage = oc.get_storage(current_resource)
487
509
  desired_storage = oc.get_storage(resource.body)
@@ -530,7 +552,14 @@ def delete(
530
552
  enable_deletion: bool,
531
553
  privileged: bool = False,
532
554
  ) -> None:
533
- logging.info(["delete", cluster, namespace, resource_type, name])
555
+ logging.info([
556
+ "delete",
557
+ f"privileged={privileged}",
558
+ cluster,
559
+ namespace,
560
+ resource_type,
561
+ name,
562
+ ])
534
563
 
535
564
  if not enable_deletion:
536
565
  logging.error("'delete' action is disabled due to previous errors.")
@@ -556,182 +585,444 @@ def check_unused_resource_types(ri):
556
585
  logging.warning(msg)
557
586
 
558
587
 
559
- def _realize_resource_data(
560
- unpacked_ri_item,
561
- dry_run,
562
- oc_map: ClusterMap,
588
+ @dataclass
589
+ class ApplyOptions:
590
+ dry_run: bool
591
+ no_dry_run_skip_compare: bool
592
+ wait_for_namespace: bool
593
+ recycle_pods: bool
594
+ take_over: bool
595
+ override_enable_deletion: bool | None
596
+ caller: str | None
597
+ all_callers: Sequence[str] | None
598
+ privileged: bool | None
599
+ enable_deletion: bool | None
600
+
601
+
602
+ def should_apply(
603
+ current: OR,
604
+ desired: OR,
563
605
  ri: ResourceInventory,
564
- take_over,
565
- caller,
566
- all_callers,
567
- wait_for_namespace,
568
- no_dry_run_skip_compare,
569
- override_enable_deletion,
570
- recycle_pods,
571
- ):
572
- cluster, namespace, resource_type, data = unpacked_ri_item
573
- actions: list[dict] = []
574
- if ri.has_error_registered(cluster=cluster):
575
- msg = ("[{}] skipping realize_data for " "cluster with errors").format(cluster)
576
- logging.error(msg)
577
- return actions
606
+ options: ApplyOptions,
607
+ cluster: str,
608
+ name: str,
609
+ namespace: str,
610
+ resource_type: str,
611
+ ) -> bool:
612
+ if not options.dry_run and options.no_dry_run_skip_compare:
613
+ msg = (
614
+ f"[{cluster}/{namespace}] skipping compare of resource"
615
+ f"'{resource_type}/{name}'"
616
+ )
617
+ logging.debug(msg)
618
+ return True
619
+ if (
620
+ not options.take_over
621
+ and options.caller
622
+ and current.caller != options.caller
623
+ and options.all_callers
624
+ and current.caller in options.all_callers
625
+ ):
626
+ ri.register_error()
627
+ logging.error(
628
+ f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present and "
629
+ f"managed by another caller: {current.caller}"
630
+ )
631
+ return False
578
632
 
579
- enable_deletion = False if ri.has_error_registered() else True
580
- # only allow to override enable_deletion if no errors were found
581
- if enable_deletion is True and override_enable_deletion is False:
582
- enable_deletion = False
633
+ if logging.getLogger().isEnabledFor(logging.DEBUG):
634
+ logging.debug("CURRENT: " + OR.serialize(OR.canonicalize(current.body)))
635
+ logging.debug("DESIRED: " + OR.serialize(OR.canonicalize(desired.body)))
583
636
 
584
- # desired items
585
- for name, d_item in data["desired"].items():
586
- c_item: OR = data["current"].get(name)
637
+ return True
587
638
 
588
- if c_item is not None:
589
- if not dry_run and no_dry_run_skip_compare:
590
- msg = ("[{}/{}] skipping compare of resource '{}/{}'.").format(
591
- cluster, namespace, resource_type, name
592
- )
593
- logging.debug(msg)
594
- else:
595
- # If resource doesn't have annotations, annotate and apply
596
- if not c_item.has_qontract_annotations():
597
- msg = (
598
- "[{}/{}] resource '{}/{}' present "
599
- "w/o annotations, annotating and applying"
600
- ).format(cluster, namespace, resource_type, name)
601
- logging.info(msg)
602
-
603
- # don't apply if there is a caller (saas file)
604
- # and this is not a take over
605
- # and current item caller is different from the current caller
606
- elif caller and not take_over and c_item.caller != caller:
607
- # if the current item is owned by a caller that no longer exists,
608
- # do nothing. the condition is nested so we fall into this condition
609
- # so we end up either applying to take ownership, or we error if the
610
- # current caller is still present
611
- if c_item.caller in all_callers:
612
- ri.register_error()
613
- logging.error(
614
- f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present and managed by another caller: {c_item.caller}"
615
- )
616
- continue
617
-
618
- # don't apply if resources match
619
- # if there is a caller (saas file) and this is a take over
620
- # we skip the equal compare as it's not covering
621
- # cases of a removed label (for example)
622
- # d_item == c_item is uncommutative
623
- elif not (caller and take_over) and d_item == c_item:
624
- msg = (
625
- "[{}/{}] resource '{}/{}' present "
626
- "and matches desired, skipping."
627
- ).format(cluster, namespace, resource_type, name)
628
- logging.debug(msg)
629
- continue
630
639
 
631
- # don't apply if sha256sum hashes match
632
- elif c_item.sha256sum() == d_item.sha256sum():
633
- if c_item.has_valid_sha256sum():
634
- msg = (
635
- "[{}/{}] resource '{}/{}' present "
636
- "and hashes match, skipping."
637
- ).format(cluster, namespace, resource_type, name)
638
- logging.debug(msg)
639
- continue
640
-
641
- msg = (
642
- "[{}/{}] resource '{}/{}' present and "
643
- "has stale sha256sum due to manual changes."
644
- ).format(cluster, namespace, resource_type, name)
645
- logging.info(msg)
646
-
647
- logging.debug("CURRENT: " + OR.serialize(OR.canonicalize(c_item.body)))
648
- else:
649
- logging.debug("CURRENT: None")
640
+ def should_delete(
641
+ current: OR,
642
+ cluster: str,
643
+ name: str,
644
+ namespace: str,
645
+ resource_type: str,
646
+ options: ApplyOptions,
647
+ ) -> bool:
648
+ if current.has_qontract_annotations():
649
+ if options.caller and current.caller != options.caller:
650
+ return False
651
+ elif not options.take_over:
652
+ # this is reached when the current resources:
653
+ # - does not have qontract annotations (not managed)
654
+ # - not taking over all resources of the current kind
655
+ msg = f"[{cluster}/{namespace}] skipping " + f"{resource_type}/{name}"
656
+ logging.debug(msg)
657
+ return False
658
+ elif current.has_owner_reference():
659
+ return False
660
+ return True
661
+
662
+
663
+ def handle_new_resources(
664
+ oc_map: ClusterMap,
665
+ ri: ResourceInventory,
666
+ new_resources: Mapping[Any, Any],
667
+ cluster: str,
668
+ namespace: str,
669
+ resource_type: str,
670
+ data: Mapping[Any, Any],
671
+ options: ApplyOptions,
672
+ ) -> list[dict[str, Any]]:
673
+ actions: list[dict[str, Any]] = []
674
+
675
+ for name, desired in new_resources.items():
676
+ options.privileged = data["use_admin_token"].get(name, False)
677
+ action = {
678
+ "action": ACTION_APPLIED,
679
+ "cluster": cluster,
680
+ "namespace": namespace,
681
+ "kind": resource_type,
682
+ "name": name,
683
+ "privileged": options.privileged,
684
+ }
685
+ actions.append(action)
686
+ apply_action(
687
+ oc_map=oc_map,
688
+ ri=ri,
689
+ cluster=cluster,
690
+ namespace=namespace,
691
+ resource_type=resource_type,
692
+ resource=desired,
693
+ options=options,
694
+ )
650
695
 
651
- logging.debug("DESIRED: " + OR.serialize(OR.canonicalize(d_item.body)))
696
+ return actions
652
697
 
653
- try:
654
- privileged = data["use_admin_token"].get(name, False)
655
- apply(
656
- dry_run,
657
- oc_map,
658
- cluster,
659
- namespace,
660
- resource_type,
661
- d_item,
662
- wait_for_namespace,
663
- recycle_pods,
664
- privileged,
698
+
699
+ def should_take_over(
700
+ current: OR,
701
+ ri: ResourceInventory,
702
+ options: ApplyOptions,
703
+ cluster: str,
704
+ name: str,
705
+ namespace: str,
706
+ resource_type: str,
707
+ ) -> bool:
708
+ if options.caller and options.caller != current.caller:
709
+ # The resources are identical but the caller is different
710
+ if options.take_over or (
711
+ options.all_callers and current.caller not in options.all_callers
712
+ ):
713
+ return True
714
+ else:
715
+ ri.register_error()
716
+ logging.error(
717
+ f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present "
718
+ f"and managed by another caller: {current.caller}"
665
719
  )
720
+ return False
721
+
722
+
723
+ def handle_identical_resources(
724
+ oc_map: ClusterMap,
725
+ ri: ResourceInventory,
726
+ identical_resources: Mapping[Any, Any],
727
+ cluster: str,
728
+ namespace: str,
729
+ resource_type: str,
730
+ data: Mapping[Any, Any],
731
+ options: ApplyOptions,
732
+ ) -> list[dict[str, Any]]:
733
+ actions: list[dict[str, Any]] = []
734
+
735
+ for name, dp in identical_resources.items():
736
+ if should_take_over(
737
+ current=dp.current,
738
+ ri=ri,
739
+ cluster=cluster,
740
+ name=name,
741
+ namespace=namespace,
742
+ resource_type=resource_type,
743
+ options=options,
744
+ ):
745
+ options.privileged = data["use_admin_token"].get(name, False)
666
746
  action = {
667
747
  "action": ACTION_APPLIED,
668
748
  "cluster": cluster,
669
749
  "namespace": namespace,
670
750
  "kind": resource_type,
671
- "name": d_item.name,
672
- "privileged": privileged,
751
+ "name": name,
752
+ "privileged": options.privileged,
673
753
  }
674
754
  actions.append(action)
675
- except StatusCodeError as e:
676
- ri.register_error()
677
- err = (
678
- str(e)
679
- if resource_type != "Secret"
680
- else f"error applying Secret {d_item.name}: REDACTED"
681
- )
682
- msg = (
683
- f"[{cluster}/{namespace}] {err} "
684
- + f"(error details: {d_item.error_details})"
755
+ apply_action(
756
+ oc_map=oc_map,
757
+ ri=ri,
758
+ cluster=cluster,
759
+ namespace=namespace,
760
+ resource_type=resource_type,
761
+ resource=dp.desired,
762
+ options=options,
685
763
  )
686
- logging.error(msg)
764
+ return actions
687
765
 
688
- # current items
689
- for name, c_item in data["current"].items():
690
- d_item = data["desired"].get(name)
691
- if d_item is not None:
692
- continue
693
766
 
694
- if c_item.has_qontract_annotations():
695
- if caller and c_item.caller != caller:
696
- continue
697
- elif not take_over:
698
- # this is reached when the current resources:
699
- # - does not have qontract annotations (not managed)
700
- # - not taking over all resources of the current kind
701
- msg = (
702
- f"[{cluster}/{namespace}] skipping " + f"{resource_type}/{c_item.name}"
767
+ def handle_modified_resources(
768
+ oc_map: ClusterMap,
769
+ ri: ResourceInventory,
770
+ modified_resources: Mapping[Any, Any],
771
+ cluster: str,
772
+ namespace: str,
773
+ resource_type: str,
774
+ data: Mapping[Any, Any],
775
+ options: ApplyOptions,
776
+ ) -> list[dict[str, Any]]:
777
+ actions: list[dict[str, Any]] = []
778
+
779
+ for name, dp in modified_resources.items():
780
+ if should_apply(
781
+ current=dp.current,
782
+ desired=dp.desired,
783
+ ri=ri,
784
+ cluster=cluster,
785
+ name=name,
786
+ namespace=namespace,
787
+ resource_type=resource_type,
788
+ options=options,
789
+ ):
790
+ options.privileged = data["use_admin_token"].get(name, False)
791
+ action = {
792
+ "action": ACTION_APPLIED,
793
+ "cluster": cluster,
794
+ "namespace": namespace,
795
+ "kind": resource_type,
796
+ "name": name,
797
+ "privileged": options.privileged,
798
+ }
799
+ actions.append(action)
800
+ apply_action(
801
+ oc_map=oc_map,
802
+ ri=ri,
803
+ cluster=cluster,
804
+ namespace=namespace,
805
+ resource_type=resource_type,
806
+ resource=dp.desired,
807
+ options=options,
703
808
  )
704
- logging.debug(msg)
705
- continue
809
+ return actions
706
810
 
707
- if c_item.has_owner_reference():
708
- continue
709
811
 
710
- try:
711
- privileged = data["use_admin_token"].get(name, False)
712
- delete(
713
- dry_run,
714
- oc_map,
715
- cluster,
716
- namespace,
717
- resource_type,
718
- name,
719
- enable_deletion,
720
- privileged,
721
- )
812
+ def handle_deleted_resources(
813
+ oc_map: ClusterMap,
814
+ ri: ResourceInventory,
815
+ deleted_resources: Mapping[Any, Any],
816
+ cluster: str,
817
+ namespace: str,
818
+ resource_type: str,
819
+ data: Mapping[Any, Any],
820
+ options: ApplyOptions,
821
+ ) -> list[dict[str, Any]]:
822
+ actions: list[dict[str, Any]] = []
823
+
824
+ for name, current in deleted_resources.items():
825
+ if should_delete(
826
+ current=current,
827
+ cluster=cluster,
828
+ name=name,
829
+ namespace=namespace,
830
+ resource_type=resource_type,
831
+ options=options,
832
+ ):
833
+ options.privileged = data["use_admin_token"].get(name, False)
722
834
  action = {
723
835
  "action": ACTION_DELETED,
724
836
  "cluster": cluster,
725
837
  "namespace": namespace,
726
838
  "kind": resource_type,
727
839
  "name": name,
728
- "privileged": privileged,
840
+ "privileged": options.privileged,
729
841
  }
730
842
  actions.append(action)
731
- except StatusCodeError as e:
732
- ri.register_error()
733
- msg = "[{}/{}] {}".format(cluster, namespace, str(e))
734
- logging.error(msg)
843
+ delete_action(
844
+ oc_map=oc_map,
845
+ ri=ri,
846
+ cluster=cluster,
847
+ namespace=namespace,
848
+ resource_type=resource_type,
849
+ resource_name=name,
850
+ options=options,
851
+ )
852
+
853
+ return actions
854
+
855
+
856
+ def apply_action(
857
+ oc_map: ClusterMap,
858
+ ri: ResourceInventory,
859
+ cluster: str,
860
+ namespace: str,
861
+ resource_type: str,
862
+ resource: OR,
863
+ options: ApplyOptions,
864
+ ) -> None:
865
+ try:
866
+ apply(
867
+ dry_run=options.dry_run,
868
+ oc_map=oc_map,
869
+ cluster=cluster,
870
+ namespace=namespace,
871
+ resource_type=resource_type,
872
+ resource=resource,
873
+ wait_for_namespace=options.wait_for_namespace,
874
+ recycle_pods=options.recycle_pods,
875
+ privileged=bool(options.privileged),
876
+ )
877
+
878
+ except StatusCodeError as e:
879
+ ri.register_error()
880
+ err = (
881
+ str(e)
882
+ if resource_type != "Secret"
883
+ else f"error applying Secret {resource.name}: REDACTED"
884
+ )
885
+ msg = (
886
+ f"[{cluster}/{namespace}] {err} "
887
+ f"(error details: {resource.error_details})"
888
+ )
889
+ logging.error(msg)
890
+
891
+
892
+ def delete_action(
893
+ oc_map: ClusterMap,
894
+ ri: ResourceInventory,
895
+ cluster: str,
896
+ namespace: str,
897
+ resource_type: str,
898
+ resource_name: str,
899
+ options: ApplyOptions,
900
+ ) -> None:
901
+ try:
902
+ delete(
903
+ dry_run=options.dry_run,
904
+ oc_map=oc_map,
905
+ cluster=cluster,
906
+ namespace=namespace,
907
+ resource_type=resource_type,
908
+ name=resource_name,
909
+ enable_deletion=bool(options.enable_deletion),
910
+ privileged=bool(options.privileged),
911
+ )
912
+ except StatusCodeError as e:
913
+ ri.register_error()
914
+ msg = f"[{cluster}/{namespace}] {e!s}"
915
+ logging.error(msg)
916
+
917
+
918
+ def _realize_resource_data(
919
+ ri_item: tuple[str, str, str, Mapping[str, Any]],
920
+ dry_run: bool,
921
+ oc_map: ClusterMap,
922
+ ri: ResourceInventory,
923
+ take_over: bool,
924
+ caller: str,
925
+ all_callers: Sequence[str],
926
+ wait_for_namespace: bool,
927
+ no_dry_run_skip_compare: bool,
928
+ override_enable_deletion: bool,
929
+ recycle_pods: bool,
930
+ ) -> list[dict[str, Any]]:
931
+ options = ApplyOptions(
932
+ dry_run=dry_run,
933
+ no_dry_run_skip_compare=no_dry_run_skip_compare,
934
+ wait_for_namespace=wait_for_namespace,
935
+ override_enable_deletion=override_enable_deletion,
936
+ take_over=take_over,
937
+ caller=caller,
938
+ all_callers=all_callers,
939
+ recycle_pods=recycle_pods,
940
+ privileged=False,
941
+ enable_deletion=False,
942
+ )
943
+ return _realize_resource_data_3way_diff(
944
+ ri_item=ri_item, oc_map=oc_map, ri=ri, options=options
945
+ )
946
+
947
+
948
+ def _realize_resource_data_3way_diff(
949
+ ri_item: tuple[str, str, str, Mapping[str, Any]],
950
+ oc_map: ClusterMap,
951
+ ri: ResourceInventory,
952
+ options: ApplyOptions,
953
+ ) -> list[dict[str, Any]]:
954
+ cluster, namespace, resource_type, data = ri_item
955
+
956
+ actions: list[dict] = []
957
+
958
+ if ri.has_error_registered(cluster=cluster):
959
+ msg = f"[{cluster}] skipping realize_data for " "cluster with errors"
960
+ logging.error(msg)
961
+ return actions
962
+
963
+ # don't delete resources if there are errors
964
+ options.enable_deletion = not ri.has_error_registered()
965
+ # only allow to override enable_deletion if no errors were found
966
+ if options.enable_deletion and options.override_enable_deletion is False:
967
+ options.enable_deletion = False
968
+
969
+ diff_result = differ.diff_mappings(
970
+ data["current"], data["desired"], equal=three_way_diff_using_hash
971
+ )
972
+
973
+ # identical resources need to be checked
974
+ # for take_overs and saas file deprecations
975
+ actions.extend(
976
+ handle_identical_resources(
977
+ oc_map=oc_map,
978
+ ri=ri,
979
+ identical_resources=diff_result.identical,
980
+ cluster=cluster,
981
+ namespace=namespace,
982
+ resource_type=resource_type,
983
+ data=data,
984
+ options=options,
985
+ )
986
+ )
987
+
988
+ actions.extend(
989
+ handle_new_resources(
990
+ oc_map=oc_map,
991
+ ri=ri,
992
+ new_resources=diff_result.add,
993
+ cluster=cluster,
994
+ namespace=namespace,
995
+ resource_type=resource_type,
996
+ data=data,
997
+ options=options,
998
+ )
999
+ )
1000
+
1001
+ actions.extend(
1002
+ handle_modified_resources(
1003
+ oc_map=oc_map,
1004
+ ri=ri,
1005
+ modified_resources=diff_result.change,
1006
+ cluster=cluster,
1007
+ namespace=namespace,
1008
+ resource_type=resource_type,
1009
+ data=data,
1010
+ options=options,
1011
+ )
1012
+ )
1013
+
1014
+ actions.extend(
1015
+ handle_deleted_resources(
1016
+ oc_map=oc_map,
1017
+ ri=ri,
1018
+ deleted_resources=diff_result.delete,
1019
+ cluster=cluster,
1020
+ namespace=namespace,
1021
+ resource_type=resource_type,
1022
+ data=data,
1023
+ options=options,
1024
+ )
1025
+ )
735
1026
 
736
1027
  return actions
737
1028
 
@@ -775,7 +1066,7 @@ def realize_data(
775
1066
 
776
1067
  def _validate_resources_used_exist(
777
1068
  ri: ResourceInventory,
778
- oc: OCDeprecated,
1069
+ oc: OCCli,
779
1070
  spec: dict[str, Any],
780
1071
  cluster: str,
781
1072
  namespace: str,
@@ -810,10 +1101,10 @@ def _validate_resources_used_exist(
810
1101
  )
811
1102
  # we found one! does it's value (secret name) match the
812
1103
  # using resource's?
813
- if used_name in (
1104
+ if used_name in {
814
1105
  serving_cert_alpha_secret_name,
815
1106
  serving_cert_beta_secret_name,
816
- ):
1107
+ }:
817
1108
  # found a match. we assume the serving cert secret will
818
1109
  # be present at some point soon after the Service is deployed
819
1110
  resource = service
@@ -850,7 +1141,7 @@ def validate_planned_data(ri: ResourceInventory, oc_map: ClusterMap) -> None:
850
1141
  oc = oc_map.get_cluster(cluster)
851
1142
 
852
1143
  for name, d_item in data["desired"].items():
853
- if kind in ("Deployment", "DeploymentConfig"):
1144
+ if kind in {"Deployment", "DeploymentConfig"}:
854
1145
  spec = d_item.body["spec"]["template"]["spec"]
855
1146
  _validate_resources_used_exist(
856
1147
  ri, oc, spec, cluster, namespace, kind, name, "Secret"
@@ -860,7 +1151,7 @@ def validate_planned_data(ri: ResourceInventory, oc_map: ClusterMap) -> None:
860
1151
  )
861
1152
 
862
1153
 
863
- @retry(exceptions=(ValidationError), max_attempts=100)
1154
+ @retry(exceptions=(ValidationError), max_attempts=200)
864
1155
  def validate_realized_data(actions: Iterable[dict[str, str]], oc_map: ClusterMap):
865
1156
  """
866
1157
  Validate the realized desired state.
@@ -897,7 +1188,7 @@ def validate_realized_data(actions: Iterable[dict[str, str]], oc_map: ClusterMap
897
1188
  if not status:
898
1189
  raise ValidationError("status")
899
1190
  # add elif to validate additional resource kinds
900
- if kind in ["Deployment", "DeploymentConfig", "StatefulSet"]:
1191
+ if kind in {"Deployment", "DeploymentConfig", "StatefulSet"}:
901
1192
  desired_replicas = resource["spec"]["replicas"]
902
1193
  if desired_replicas == 0:
903
1194
  continue
@@ -1038,16 +1329,95 @@ def aggregate_shared_resources(namespace_info, shared_resources_type):
1038
1329
  namespace_info[shared_resources_type] = namespace_type_resources
1039
1330
 
1040
1331
 
1332
+ @runtime_checkable
1333
+ class HasOpenShiftResources(Protocol):
1334
+ openshift_resources: list | None
1335
+
1336
+
1337
+ @runtime_checkable
1338
+ class HasOpenshiftServiceAccountTokens(Protocol):
1339
+ openshift_service_account_tokens: list | None
1340
+
1341
+
1342
+ @runtime_checkable
1343
+ class HasSharedResourcesOpenShiftResources(Protocol):
1344
+ @property
1345
+ def shared_resources(self) -> Sequence[HasOpenShiftResources] | None:
1346
+ pass
1347
+
1348
+
1349
+ @runtime_checkable
1350
+ class HasSharedResourcesOpenshiftServiceAccountTokens(Protocol):
1351
+ @property
1352
+ def shared_resources(self) -> Sequence[HasOpenshiftServiceAccountTokens] | None:
1353
+ pass
1354
+
1355
+
1356
+ @runtime_checkable
1357
+ class HasOpenshiftServiceAccountTokensAndSharedResources(
1358
+ HasOpenshiftServiceAccountTokens,
1359
+ HasSharedResourcesOpenshiftServiceAccountTokens,
1360
+ Protocol,
1361
+ ): ...
1362
+
1363
+
1364
+ @runtime_checkable
1365
+ class HasOpenShiftResourcesAndSharedResources(
1366
+ HasOpenShiftResources, HasSharedResourcesOpenShiftResources, Protocol
1367
+ ): ...
1368
+
1369
+
1370
+ def aggregate_shared_resources_typed(
1371
+ namespace: HasOpenshiftServiceAccountTokensAndSharedResources
1372
+ | HasOpenShiftResourcesAndSharedResources,
1373
+ ) -> None:
1374
+ """This function aggregates the shared resources to the appropriate namespace section.
1375
+
1376
+ Attention: It updates the namespace object in place and isn't indempotent!
1377
+ """
1378
+ if not namespace.shared_resources:
1379
+ return
1380
+
1381
+ match namespace:
1382
+ case HasOpenshiftServiceAccountTokensAndSharedResources():
1383
+ shared_type_resources_items = []
1384
+ for ost_shared_resources_item in namespace.shared_resources:
1385
+ if (
1386
+ shared_type_resources
1387
+ := ost_shared_resources_item.openshift_service_account_tokens
1388
+ ):
1389
+ shared_type_resources_items.extend(shared_type_resources)
1390
+ if namespace.openshift_service_account_tokens:
1391
+ namespace.openshift_service_account_tokens.extend(
1392
+ shared_type_resources_items
1393
+ )
1394
+ else:
1395
+ namespace.openshift_service_account_tokens = shared_type_resources_items
1396
+ case HasOpenShiftResourcesAndSharedResources():
1397
+ shared_type_resources_items = []
1398
+ for or_shared_resources_item in namespace.shared_resources:
1399
+ if (
1400
+ shared_type_resources
1401
+ := or_shared_resources_item.openshift_resources
1402
+ ):
1403
+ shared_type_resources_items.extend(shared_type_resources)
1404
+ if namespace.openshift_resources:
1405
+ namespace.openshift_resources.extend(shared_type_resources_items)
1406
+ else:
1407
+ namespace.openshift_resources = shared_type_resources_items
1408
+
1409
+
1041
1410
  def determine_user_keys_for_access(
1042
1411
  cluster_name: str,
1043
- auth_list: Sequence[Union[dict[str, str], HasService]],
1044
- enforced_user_keys: Optional[list[str]] = None,
1412
+ auth_list: Sequence[dict[str, str] | HasService],
1413
+ enforced_user_keys: list[str] | None = None,
1045
1414
  ) -> list[str]:
1046
1415
  """Return user keys based on enabled cluster authentication methods."""
1047
1416
  AUTH_METHOD_USER_KEY = {
1048
1417
  "github-org": "github_username",
1049
1418
  "github-org-team": "github_username",
1050
1419
  "oidc": "org_username",
1420
+ "rhidp": "org_username",
1051
1421
  }
1052
1422
  user_keys: list[str] = []
1053
1423
 
@@ -1060,10 +1430,7 @@ def determine_user_keys_for_access(
1060
1430
  return ["github_username"]
1061
1431
 
1062
1432
  for auth in auth_list:
1063
- if isinstance(auth, HasService):
1064
- service = auth.service
1065
- else:
1066
- service = auth["service"]
1433
+ service = auth.service if isinstance(auth, HasService) else auth["service"]
1067
1434
  try:
1068
1435
  if AUTH_METHOD_USER_KEY[service] in user_keys:
1069
1436
  continue
@@ -1071,7 +1438,7 @@ def determine_user_keys_for_access(
1071
1438
  except KeyError:
1072
1439
  raise NotImplementedError(
1073
1440
  f"[{cluster_name}] auth service not implemented: {service}"
1074
- )
1441
+ ) from None
1075
1442
  return user_keys
1076
1443
 
1077
1444
 
@@ -1086,7 +1453,7 @@ def user_has_cluster_access(
1086
1453
  ) -> bool:
1087
1454
  """Check user has access to cluster."""
1088
1455
  userkeys = determine_user_keys_for_access(cluster.name, cluster.auth)
1089
- return any((getattr(user, userkey) in cluster_users for userkey in userkeys))
1456
+ return any(getattr(user, userkey) in cluster_users for userkey in userkeys)
1090
1457
 
1091
1458
 
1092
1459
  def get_namespace_type_overrides(namespace: Mapping) -> dict[str, str]:
@@ -1102,7 +1469,7 @@ def get_namespace_type_overrides(namespace: Mapping) -> dict[str, str]:
1102
1469
 
1103
1470
 
1104
1471
  def get_namespace_resource_types(
1105
- namespace: Mapping, type_overrides: Optional[dict[str, str]] = None
1472
+ namespace: Mapping, type_overrides: dict[str, str] | None = None
1106
1473
  ) -> list[str]:
1107
1474
  """Returns a list with the namespace ResourceTypes, with the overrides in place
1108
1475
 
@@ -1119,7 +1486,7 @@ def get_namespace_resource_types(
1119
1486
 
1120
1487
 
1121
1488
  def get_namespace_resource_names(
1122
- namespace: Mapping, type_overrides: Optional[dict[str, str]] = None
1489
+ namespace: Mapping, type_overrides: dict[str, str] | None = None
1123
1490
  ) -> dict[str, list[str]]:
1124
1491
  """Returns a list with the namespace ResourceTypeNames, with the overrides in place
1125
1492
 
@@ -1137,3 +1504,38 @@ def get_namespace_resource_names(
1137
1504
  ref += item["resourceNames"]
1138
1505
 
1139
1506
  return rnames
1507
+
1508
+
1509
+ def publish_metrics(ri: ResourceInventory, integration: str) -> None:
1510
+ for cluster, namespace, kind, data in ri:
1511
+ for state in ("current", "desired"):
1512
+ metrics.set_gauge(
1513
+ OpenshiftResourceInventoryGauge(
1514
+ integration=integration.replace("_", "-"),
1515
+ cluster=cluster,
1516
+ namespace=namespace,
1517
+ kind=kind,
1518
+ state=state,
1519
+ ),
1520
+ len(data[state]),
1521
+ )
1522
+
1523
+
1524
+ def get_state_count_combinations(state: Iterable[Mapping[str, str]]) -> Counter[str]:
1525
+ return Counter(s["cluster"] for s in state)
1526
+
1527
+
1528
+ def publish_cluster_desired_metrics_from_state(
1529
+ state: Iterable[Mapping[str, str]], integration: str, kind: str
1530
+ ) -> None:
1531
+ for cluster, count in get_state_count_combinations(state).items():
1532
+ metrics.set_gauge(
1533
+ OpenshiftResourceInventoryGauge(
1534
+ integration=integration,
1535
+ cluster=cluster,
1536
+ namespace="cluster",
1537
+ kind=kind,
1538
+ state="desired",
1539
+ ),
1540
+ count,
1541
+ )