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
@@ -0,0 +1,340 @@
1
+ import logging
2
+ import sys
3
+ from collections.abc import Callable, Iterable
4
+ from enum import IntFlag, auto
5
+ from typing import Any, TypedDict
6
+
7
+ from jira import JIRAError
8
+ from pydantic import BaseModel
9
+
10
+ from reconcile.gql_definitions.jira_permissions_validator.jira_boards_for_permissions_validator import (
11
+ JiraBoardV1,
12
+ )
13
+ from reconcile.gql_definitions.jira_permissions_validator.jira_boards_for_permissions_validator import (
14
+ query as query_jira_boards,
15
+ )
16
+ from reconcile.status import ExitCodes
17
+ from reconcile.typed_queries.app_interface_vault_settings import (
18
+ get_app_interface_vault_settings,
19
+ )
20
+ from reconcile.typed_queries.jira_settings import get_jira_settings
21
+ from reconcile.typed_queries.jiralert_settings import get_jiralert_settings
22
+ from reconcile.utils import gql, metrics
23
+ from reconcile.utils.disabled_integrations import integration_is_enabled
24
+ from reconcile.utils.extended_early_exit import (
25
+ ExtendedEarlyExitRunnerResult,
26
+ extended_early_exit_run,
27
+ )
28
+ from reconcile.utils.jira_client import JiraClient, JiraWatcherSettings
29
+ from reconcile.utils.secret_reader import SecretReaderBase, create_secret_reader
30
+ from reconcile.utils.semver_helper import make_semver
31
+ from reconcile.utils.unleash import get_feature_toggle_state
32
+
33
+ QONTRACT_INTEGRATION = "jira-permissions-validator"
34
+ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
35
+
36
+ NameToIdMap = dict[str, str]
37
+
38
+
39
+ class BaseMetric(BaseModel):
40
+ """Base class for metrics"""
41
+
42
+ jira_server: str
43
+ board: str
44
+
45
+
46
+ class PermissionErrorCounter(BaseMetric, metrics.GaugeMetric):
47
+ """Boards with permission errors."""
48
+
49
+ @classmethod
50
+ def name(cls) -> str:
51
+ return "jira_permissions_validator_permission_error"
52
+
53
+
54
+ class ValidationError(IntFlag):
55
+ CANT_CREATE_ISSUE = auto()
56
+ CANT_TRANSITION_ISSUES = auto()
57
+ INVALID_ISSUE_TYPE = auto()
58
+ INVALID_ISSUE_STATE = auto()
59
+ INVALID_SECURITY_LEVEL = auto()
60
+ INVALID_PRIORITY = auto()
61
+ PERMISSION_ERROR = auto()
62
+ PUBLIC_PROJECT_NO_SECURITY_LEVEL = auto()
63
+ INVALID_COMPONENT = auto()
64
+ PROJECT_ARCHIVED = auto()
65
+
66
+
67
+ class RunnerParams(TypedDict):
68
+ boards: list[JiraBoardV1]
69
+ dry_run: bool
70
+
71
+
72
+ class CacheSource(TypedDict):
73
+ boards: list
74
+
75
+
76
+ def board_is_valid(
77
+ jira: JiraClient,
78
+ board: JiraBoardV1,
79
+ default_issue_type: str,
80
+ default_reopen_state: str,
81
+ jira_server_priorities: NameToIdMap,
82
+ public_projects: Iterable[str],
83
+ ) -> ValidationError:
84
+ error = ValidationError(0)
85
+ try:
86
+ if jira.is_archived:
87
+ logging.error(f"[{board.name}] project is archived")
88
+ return ValidationError.PROJECT_ARCHIVED
89
+
90
+ if not jira.can_create_issues():
91
+ logging.error(f"[{board.name}] can not create issues in project")
92
+ error |= ValidationError.CANT_CREATE_ISSUE
93
+
94
+ if not jira.can_transition_issues():
95
+ logging.error(
96
+ f"[{board.name}] AppSRE Jira Bot user does not have the permission to change the issue status."
97
+ )
98
+ error |= ValidationError.CANT_TRANSITION_ISSUES
99
+
100
+ components = jira.components()
101
+ for escalation_policy in board.escalation_policies or []:
102
+ jira_component = escalation_policy.channels.jira_component
103
+ if jira_component and jira_component not in components:
104
+ logging.error(
105
+ f"[{board.name}] escalation policy '{escalation_policy.name}' references a non existing Jira component "
106
+ f"'{jira_component}'. Valid components: {components}"
107
+ )
108
+ error |= ValidationError.INVALID_COMPONENT
109
+
110
+ issue_type = board.issue_type if board.issue_type else default_issue_type
111
+ project_issue_types = jira.project_issue_types()
112
+ project_issue_types_str = [i.name for i in project_issue_types]
113
+ if issue_type not in project_issue_types_str:
114
+ logging.error(
115
+ f"[{board.name}] {issue_type} is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
116
+ )
117
+ error |= ValidationError.INVALID_ISSUE_TYPE
118
+
119
+ available_states = []
120
+ for project_issue_type in project_issue_types:
121
+ if issue_type == project_issue_type.name:
122
+ available_states = project_issue_type.statuses
123
+ break
124
+
125
+ if not available_states:
126
+ logging.error(
127
+ f"[{board.name}] {issue_type} doesn't have any status. Choose a different issue type."
128
+ )
129
+ error |= ValidationError.INVALID_ISSUE_TYPE
130
+
131
+ reopen_state = (
132
+ board.issue_reopen_state
133
+ if board.issue_reopen_state
134
+ else default_reopen_state
135
+ )
136
+ if reopen_state.lower() not in [t.lower() for t in available_states]:
137
+ logging.error(
138
+ f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {available_states}"
139
+ )
140
+ error |= ValidationError.INVALID_ISSUE_STATE
141
+
142
+ if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
143
+ t.lower() for t in available_states
144
+ ]:
145
+ logging.error(
146
+ f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {available_states}"
147
+ )
148
+ error |= ValidationError.INVALID_ISSUE_STATE
149
+
150
+ if board.issue_security_id:
151
+ security_levels = jira.security_levels()
152
+ if board.issue_security_id not in [level.id for level in security_levels]:
153
+ logging.error(
154
+ f"[{board.name}] {board.issue_security_id} is not a valid security level in project. Valid security ids: "
155
+ + ", ".join([
156
+ f"{level.name} - {level.id}" for level in jira.security_levels()
157
+ ])
158
+ )
159
+ error |= ValidationError.INVALID_SECURITY_LEVEL
160
+ elif board.name in public_projects:
161
+ logging.error(
162
+ f"[{board.name}] is a public project, but no security level is defined."
163
+ )
164
+ error |= ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
165
+
166
+ project_priorities = jira.project_priority_scheme()
167
+ # get the priority names from the project priorities ids
168
+ project_priorities_names = [
169
+ p_name
170
+ for project_p_id in project_priorities
171
+ for p_name, p_id in jira_server_priorities.items()
172
+ if p_id == project_p_id
173
+ ]
174
+ for priority in board.severity_priority_mappings.mappings:
175
+ if priority.priority not in jira_server_priorities:
176
+ logging.error(
177
+ f"[{board.name}] {priority.priority} is not a valid Jira priority. Valid priorities: {project_priorities_names}"
178
+ )
179
+ error |= ValidationError.INVALID_PRIORITY
180
+ continue
181
+ if jira_server_priorities[priority.priority] not in project_priorities:
182
+ logging.error(
183
+ f"[{board.name}] {priority.priority} is not a valid priority in project. Valid priorities: {project_priorities_names}"
184
+ )
185
+ error |= ValidationError.INVALID_PRIORITY
186
+ except JIRAError as e:
187
+ if e.status_code == 401:
188
+ # sporadic 401 errors, retrying
189
+ logging.debug(f"[{board.name}] sporadic 401 error! Retry later.")
190
+ elif e.status_code == 403:
191
+ logging.error(
192
+ f"[{board.name}] AppSRE Jira Bot user does not have all necessary permissions. Try granting the user the administrator permissions. API URL: {e.url}"
193
+ )
194
+ error |= ValidationError.PERMISSION_ERROR
195
+ else:
196
+ raise
197
+
198
+ return error
199
+
200
+
201
+ def validate_boards(
202
+ metrics_container: metrics.MetricsContainer,
203
+ secret_reader: SecretReaderBase,
204
+ jira_client_settings: JiraWatcherSettings | None,
205
+ jira_boards: Iterable[JiraBoardV1],
206
+ default_issue_type: str,
207
+ default_reopen_state: str,
208
+ dry_run: bool,
209
+ jira_client_class: type[JiraClient] = JiraClient,
210
+ ) -> bool:
211
+ error = False
212
+ jira_clients: dict[str, JiraClient] = {}
213
+ for board in jira_boards:
214
+ logging.debug(f"[{board.name}] checking ...")
215
+ if board.server.server_url not in jira_clients:
216
+ jira_clients[board.server.server_url] = jira_client_class.create(
217
+ project_name=board.name,
218
+ token=secret_reader.read_secret(board.server.token),
219
+ server_url=board.server.server_url,
220
+ jira_watcher_settings=jira_client_settings,
221
+ )
222
+ jira = jira_clients[board.server.server_url]
223
+ jira.project = board.name
224
+ try:
225
+ error_flags = board_is_valid(
226
+ jira=jira,
227
+ board=board,
228
+ default_issue_type=default_issue_type,
229
+ default_reopen_state=default_reopen_state,
230
+ jira_server_priorities={p.name: p.id for p in jira.priorities()},
231
+ public_projects=jira.public_projects(),
232
+ )
233
+ match error_flags:
234
+ case 0:
235
+ # no errors
236
+ logging.debug(f"[{board.name}] is valid")
237
+ case ValidationError.PERMISSION_ERROR:
238
+ # we don't have all the permissions, but we can create jira tickets
239
+ metrics_container.set_gauge(
240
+ PermissionErrorCounter(
241
+ jira_server=board.server.server_url,
242
+ board=board.name,
243
+ ),
244
+ value=1,
245
+ )
246
+ if dry_run:
247
+ # throw an error for MR checks but not in prod mode
248
+ error = True
249
+ case (
250
+ ValidationError.CANT_CREATE_ISSUE | ValidationError.PROJECT_ARCHIVED
251
+ ):
252
+ if dry_run:
253
+ # throw an error for MR checks but not in prod mode
254
+ error = True
255
+ case _:
256
+ error = True
257
+ except Exception as e:
258
+ logging.error(f"[{board.name}] {e}")
259
+ error = True
260
+ return error
261
+
262
+
263
+ def get_jira_boards(query_func: Callable) -> list[JiraBoardV1]:
264
+ return [
265
+ board
266
+ for board in query_jira_boards(query_func=query_func).jira_boards or []
267
+ if integration_is_enabled(QONTRACT_INTEGRATION, board)
268
+ ]
269
+
270
+
271
+ def export_boards(boards: list[JiraBoardV1]) -> list[dict]:
272
+ return [board.dict() for board in boards]
273
+
274
+
275
+ def run(
276
+ dry_run: bool,
277
+ enable_extended_early_exit: bool = False,
278
+ extended_early_exit_cache_ttl_seconds: int = 3600,
279
+ log_cached_log_output: bool = False,
280
+ ) -> None:
281
+ gql_api = gql.get_api()
282
+ boards = get_jira_boards(query_func=gql_api.query)
283
+ runner_params: RunnerParams = {
284
+ "boards": boards,
285
+ "dry_run": dry_run,
286
+ }
287
+ if enable_extended_early_exit and get_feature_toggle_state(
288
+ "jira-permissions-validator-extended-early-exit",
289
+ default=True,
290
+ ):
291
+ vault_settings = get_app_interface_vault_settings()
292
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
293
+
294
+ cache_source = CacheSource(
295
+ boards=export_boards(boards),
296
+ )
297
+ extended_early_exit_run(
298
+ integration=QONTRACT_INTEGRATION,
299
+ integration_version=QONTRACT_INTEGRATION_VERSION,
300
+ # don't use `dry_run` in the cache key because this is a read-only integration
301
+ dry_run=False,
302
+ cache_source=cache_source,
303
+ shard="",
304
+ ttl_seconds=extended_early_exit_cache_ttl_seconds,
305
+ logger=logging.getLogger(),
306
+ runner=runner,
307
+ runner_params=runner_params,
308
+ secret_reader=secret_reader,
309
+ log_cached_log_output=log_cached_log_output,
310
+ )
311
+ else:
312
+ runner(**runner_params)
313
+
314
+
315
+ def runner(boards: list[JiraBoardV1], dry_run: bool) -> ExtendedEarlyExitRunnerResult:
316
+ gql_api = gql.get_api()
317
+ settings = get_jira_settings(gql_api=gql_api)
318
+ jiralert_settings = get_jiralert_settings(query_func=gql_api.query)
319
+ vault_settings = get_app_interface_vault_settings()
320
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
321
+
322
+ with metrics.transactional_metrics("jira-boards") as metrics_container:
323
+ error = validate_boards(
324
+ metrics_container=metrics_container,
325
+ secret_reader=secret_reader,
326
+ jira_client_settings=settings.jira_watcher,
327
+ jira_boards=boards,
328
+ default_issue_type=jiralert_settings.default_issue_type,
329
+ default_reopen_state=jiralert_settings.default_reopen_state,
330
+ dry_run=dry_run,
331
+ )
332
+
333
+ if error:
334
+ sys.exit(ExitCodes.ERROR)
335
+
336
+ return ExtendedEarlyExitRunnerResult(payload=export_boards(boards), applied_count=0)
337
+
338
+
339
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
340
+ return {"boards": export_boards(get_jira_boards(query_func=gql.get_api().query))}
reconcile/jira_watcher.py CHANGED
@@ -34,8 +34,8 @@ def format_message(server, key, data, event, previous_state=None, current_state=
34
34
  if previous_state and current_state
35
35
  else ""
36
36
  )
37
- url = "{}/browse/{}".format(server, key)
38
- return "{} ({}) {}{}".format(url, summary, event, info)
37
+ url = f"{server}/browse/{key}"
38
+ return f"{url} ({summary}) {event}{info}"
39
39
 
40
40
 
41
41
  def calculate_diff(server, current_state, previous_state):
@@ -55,9 +55,7 @@ def calculate_diff(server, current_state, previous_state):
55
55
  messages.extend(deleted_issues)
56
56
 
57
57
  updated_issues = [
58
- format_message(
59
- server, key, data, "status change", previous_state[key], current_state[key]
60
- )
58
+ format_message(server, key, data, "status change", previous_state[key], data)
61
59
  for key, data in current_state.items()
62
60
  if key in previous_state and data["status"] != previous_state[key]["status"]
63
61
  ]
File without changes
@@ -0,0 +1,279 @@
1
+ import contextlib
2
+ import logging
3
+ from collections.abc import (
4
+ Callable,
5
+ Iterable,
6
+ )
7
+ from typing import Any
8
+
9
+ from reconcile.gql_definitions.ldap_groups.roles import RoleV1
10
+ from reconcile.gql_definitions.ldap_groups.roles import query as roles_query
11
+ from reconcile.gql_definitions.ldap_groups.settings import LdapGroupsSettingsV1
12
+ from reconcile.gql_definitions.ldap_groups.settings import query as settings_query
13
+ from reconcile.utils import gql
14
+ from reconcile.utils.aws_helper import unique_sso_aws_accounts
15
+ from reconcile.utils.defer import defer
16
+ from reconcile.utils.differ import diff_iterables
17
+ from reconcile.utils.exceptions import (
18
+ AppInterfaceLdapGroupsSettingsError,
19
+ AppInterfaceSettingsError,
20
+ )
21
+ from reconcile.utils.helpers import find_duplicates
22
+ from reconcile.utils.internal_groups.client import (
23
+ InternalGroupsClient,
24
+ NotFound,
25
+ )
26
+ from reconcile.utils.internal_groups.models import (
27
+ Entity,
28
+ EntityType,
29
+ Group,
30
+ )
31
+ from reconcile.utils.runtime.integration import (
32
+ PydanticRunParams,
33
+ QontractReconcileIntegration,
34
+ )
35
+ from reconcile.utils.state import (
36
+ State,
37
+ init_state,
38
+ )
39
+
40
+ QONTRACT_INTEGRATION = "ldap-groups"
41
+
42
+
43
+ class LdapGroupsIntegrationParams(PydanticRunParams):
44
+ aws_sso_namespace: str
45
+
46
+
47
+ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationParams]):
48
+ """Manage LDAP groups based on App-Interface roles."""
49
+
50
+ @property
51
+ def name(self) -> str:
52
+ return QONTRACT_INTEGRATION
53
+
54
+ def get_early_exit_desired_state(
55
+ self, query_func: Callable | None = None
56
+ ) -> dict[str, Any]:
57
+ """Return the desired state for early exit."""
58
+ if not query_func:
59
+ query_func = gql.get_api().query
60
+ return {"roles": [c.dict() for c in self.get_roles(query_func)]}
61
+
62
+ @defer
63
+ def run(self, dry_run: bool, defer: Callable | None = None) -> None:
64
+ """Run the integration."""
65
+ gql_api = gql.get_api()
66
+ roles = self.get_roles(gql_api.query)
67
+ if not roles:
68
+ logging.debug("No roles found.")
69
+ return
70
+ self.settings = self.get_integration_settings(gql_api.query)
71
+ secret = self.secret_reader.read_all_secret(self.settings.credentials)
72
+
73
+ # APIs
74
+ state_obj = init_state(integration=self.name, secret_reader=self.secret_reader)
75
+ internal_groups_client = InternalGroupsClient(
76
+ secret["api_url"],
77
+ secret["issuer_url"],
78
+ secret["client_id"],
79
+ secret["client_secret"],
80
+ )
81
+ if defer:
82
+ defer(state_obj.cleanup)
83
+ defer(internal_groups_client.close)
84
+
85
+ # run
86
+ owner = Entity(
87
+ type=EntityType.SERVICE_ACCOUNT,
88
+ # OIDC service accounts are named service-account-<client_id>
89
+ id=f'service-account-{secret["client_id"]}',
90
+ )
91
+ desired_groups_for_roles = self.get_desired_groups_for_roles(
92
+ roles,
93
+ contact_list=self.settings.contact_list,
94
+ default_owners=[owner],
95
+ )
96
+ desired_groups_for_aws_roles = self.get_desired_groups_for_aws_roles(
97
+ roles,
98
+ contact_list=self.settings.contact_list,
99
+ default_owners=[owner],
100
+ )
101
+ desired_groups = desired_groups_for_roles + desired_groups_for_aws_roles
102
+
103
+ group_names = self.get_managed_groups(state_obj)
104
+ # take desired groups into account to support overtaking existing ones
105
+ group_names.update(g.name for g in desired_groups)
106
+ current_groups = self.fetch_current_state(
107
+ internal_groups_client, group_names=group_names
108
+ )
109
+ try:
110
+ self.reconcile(
111
+ dry_run=dry_run,
112
+ internal_groups_client=internal_groups_client,
113
+ desired_groups=desired_groups,
114
+ current_groups=current_groups,
115
+ )
116
+ finally:
117
+ self.set_managed_groups(dry_run=dry_run, state_obj=state_obj)
118
+
119
+ def get_managed_groups(self, state_obj: State) -> set[str]:
120
+ """Return the managed groups from the state object."""
121
+ try:
122
+ return set(state_obj["managed_groups"])
123
+ except KeyError:
124
+ return set()
125
+
126
+ def set_managed_groups(self, dry_run: bool, state_obj: State) -> None:
127
+ """Set the managed groups in the state object."""
128
+ if not dry_run:
129
+ state_obj["managed_groups"] = sorted(self._managed_groups)
130
+
131
+ @staticmethod
132
+ def get_integration_settings(query_func: Callable) -> LdapGroupsSettingsV1:
133
+ """Return the integration settings."""
134
+ data = settings_query(query_func)
135
+ if not data.settings:
136
+ raise AppInterfaceSettingsError("No app-interface settings found.")
137
+ if not data.settings[0].ldap_groups:
138
+ raise AppInterfaceLdapGroupsSettingsError(
139
+ "No app-interface ldap-groups settings found."
140
+ )
141
+ return data.settings[0].ldap_groups
142
+
143
+ def get_roles(self, query_func: Callable) -> list[RoleV1]:
144
+ """Return the roles with ldap_group set."""
145
+ data = roles_query(query_func, variables={})
146
+ roles = list(data.roles or [])
147
+ if duplicates := find_duplicates(
148
+ role.ldap_group.name for role in roles if role.ldap_group
149
+ ):
150
+ for dup in duplicates:
151
+ logging.error(f"{dup} is already in use by another role.")
152
+ raise ValueError("Duplicate ldapGroup value(s) found.")
153
+ return roles
154
+
155
+ def get_desired_groups_for_roles(
156
+ self,
157
+ roles: Iterable[RoleV1],
158
+ default_owners: list[Entity],
159
+ contact_list: str,
160
+ ) -> list[Group]:
161
+ """Return the desired rover groups for the given roles."""
162
+ groups = []
163
+ for role in roles:
164
+ if not role.ldap_group:
165
+ continue
166
+ members = [
167
+ Entity(type=EntityType.USER, id=user.org_username)
168
+ for user in role.users
169
+ ]
170
+ groups.append(
171
+ Group(
172
+ name=role.ldap_group.name,
173
+ description="Persisted App-Interface role. Managed by qontract-reconcile",
174
+ notes=role.ldap_group.notes,
175
+ display_name=f"{role.ldap_group.name} (App-Interface))",
176
+ members=members,
177
+ owners=default_owners
178
+ if not role.ldap_group.members_are_owners
179
+ else default_owners + members,
180
+ contact_list=contact_list,
181
+ )
182
+ )
183
+ return groups
184
+
185
+ def get_desired_groups_for_aws_roles(
186
+ self,
187
+ roles: Iterable[RoleV1],
188
+ default_owners: Iterable[Entity],
189
+ contact_list: str,
190
+ ) -> list[Group]:
191
+ """Return the desired rover groups for all AWS roles."""
192
+ groups = []
193
+ for role in roles:
194
+ if not role.users or (not role.aws_groups and not role.user_policies):
195
+ continue
196
+ user_policies = role.user_policies or []
197
+ aws_groups = role.aws_groups or []
198
+ for account in unique_sso_aws_accounts(
199
+ integration=self.name,
200
+ accounts=[i.account for i in user_policies + aws_groups],
201
+ ):
202
+ group_name = (
203
+ f"{self.params.aws_sso_namespace}-{account.uid}-{role.name}"
204
+ )
205
+ groups.append(
206
+ Group(
207
+ name=group_name,
208
+ description=f"AWS account: '{account.name}' Role: '{role.name}' Managed by qontract-reconcile",
209
+ display_name=group_name,
210
+ members=[
211
+ Entity(type=EntityType.USER, id=user.org_username)
212
+ for user in role.users
213
+ ],
214
+ # only owners can modify the group (e.g. add/remove members)
215
+ owners=default_owners,
216
+ contact_list=contact_list,
217
+ )
218
+ )
219
+
220
+ return groups
221
+
222
+ def fetch_current_state(
223
+ self, internal_groups_client: InternalGroupsClient, group_names: Iterable[str]
224
+ ) -> list[Group]:
225
+ """Reach out to the internal groups API and fetch all managed groups."""
226
+ groups = []
227
+ for group_name in group_names:
228
+ with contextlib.suppress(NotFound):
229
+ groups.append(internal_groups_client.group(group_name))
230
+ return groups
231
+
232
+ def reconcile(
233
+ self,
234
+ dry_run: bool,
235
+ internal_groups_client: InternalGroupsClient,
236
+ desired_groups: Iterable[Group],
237
+ current_groups: Iterable[Group],
238
+ ) -> None:
239
+ """Reach out to the internal groups API and reconcile the groups."""
240
+ diff_result = diff_iterables(
241
+ current_groups,
242
+ desired_groups,
243
+ key=lambda g: g.name,
244
+ equal=lambda g1, g2: g1 == g2,
245
+ )
246
+ # Internal Groups API does not support listing all managed groups, therefore
247
+ # we need to keep track of them ourselves.
248
+ self._managed_groups = {g.name for g in current_groups}
249
+
250
+ for group_to_add in diff_result.add.values():
251
+ logging.info([
252
+ "create_ldap_group",
253
+ group_to_add.name,
254
+ f"members={', '.join(u.id for u in group_to_add.members)}",
255
+ f"owners={', '.join(u.id for u in group_to_add.owners)}",
256
+ f"notes={group_to_add.notes}",
257
+ ])
258
+ if not dry_run:
259
+ internal_groups_client.create_group(group_to_add)
260
+ self._managed_groups.add(group_to_add.name)
261
+
262
+ for group_to_remove in diff_result.delete.values():
263
+ logging.info(["delete_ldap_group", group_to_remove.name])
264
+ if not dry_run:
265
+ with contextlib.suppress(NotFound):
266
+ internal_groups_client.delete_group(group_to_remove.name)
267
+ self._managed_groups.remove(group_to_remove.name)
268
+
269
+ for diff_pair in diff_result.change.values():
270
+ group_to_update = diff_pair.desired
271
+ logging.info([
272
+ "update_ldap_group",
273
+ group_to_update.name,
274
+ f"members={', '.join(u.id for u in group_to_update.members)}",
275
+ f"owners={', '.join(u.id for u in group_to_update.owners)}",
276
+ f"notes={group_to_update.notes}",
277
+ ])
278
+ if not dry_run:
279
+ internal_groups_client.update_group(group_to_update)
reconcile/ldap_users.py CHANGED
@@ -37,6 +37,9 @@ def init_users() -> list[dict[str, list]]:
37
37
  for a in user.get("aws_accounts", []):
38
38
  item = {"type": PathTypes.AWS_ACCOUNTS, "path": "data" + a["path"]}
39
39
  users[u].append(item)
40
+ for s in user.get("schedules"):
41
+ item = {"type": PathTypes.SCHEDULE, "path": "data" + s["path"]}
42
+ users[u].append(item)
40
43
 
41
44
  return [{"username": username, "paths": paths} for username, paths in users.items()]
42
45