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,20 +1,42 @@
1
1
  import logging
2
2
  import os
3
+ import re
4
+ from collections.abc import (
5
+ Iterable,
6
+ Set,
7
+ )
8
+ from functools import cached_property
3
9
  from operator import (
4
10
  attrgetter,
5
11
  itemgetter,
6
12
  )
7
13
  from typing import (
8
14
  Any,
9
- Optional,
15
+ TypedDict,
16
+ cast,
10
17
  )
11
18
  from urllib.parse import urlparse
12
19
 
13
20
  import gitlab
14
21
  import urllib3
22
+ from gitlab.const import (
23
+ DEVELOPER_ACCESS,
24
+ GUEST_ACCESS,
25
+ MAINTAINER_ACCESS,
26
+ OWNER_ACCESS,
27
+ REPORTER_ACCESS,
28
+ )
15
29
  from gitlab.v4.objects import (
16
30
  CurrentUser,
31
+ Group,
32
+ GroupMember,
33
+ PersonalAccessToken,
34
+ Project,
35
+ ProjectIssue,
36
+ ProjectIssueManager,
17
37
  ProjectMergeRequest,
38
+ ProjectMergeRequestManager,
39
+ ProjectMergeRequestNote,
18
40
  )
19
41
  from sretoolbox.utils import retry
20
42
 
@@ -30,6 +52,7 @@ MR_DESCRIPTION_COMMENT_ID = 0
30
52
 
31
53
  # The default value is there for unit test
32
54
  INTEGRATION_NAME = os.getenv("INTEGRATION_NAME", "")
55
+ DEFAULT_MAIN_BRANCH = "master"
33
56
 
34
57
 
35
58
  class MRState:
@@ -58,12 +81,20 @@ class MRStatus:
58
81
  CANNOT_BE_MERGED_RECHECK = "cannot_be_merged_recheck"
59
82
 
60
83
 
84
+ GROUP_BOT_NAME_REGEX = re.compile(r"group_.+_bot_.+")
85
+
86
+
87
+ class GLGroupMember(TypedDict):
88
+ id: str
89
+ user: str
90
+ access_level: str
91
+
92
+
61
93
  class GitLabApi: # pylint: disable=too-many-public-methods
62
94
  def __init__(
63
95
  self,
64
96
  instance,
65
97
  project_id=None,
66
- ssl_verify=True,
67
98
  settings=None,
68
99
  secret_reader=None,
69
100
  project_url=None,
@@ -74,11 +105,14 @@ class GitLabApi: # pylint: disable=too-many-public-methods
74
105
  if not secret_reader:
75
106
  secret_reader = SecretReader(settings=settings)
76
107
  token = secret_reader.read(instance["token"])
77
- ssl_verify = instance["sslVerify"]
78
- if ssl_verify is None:
79
- ssl_verify = True
108
+ self.ssl_verify = instance["sslVerify"]
109
+ if self.ssl_verify is None:
110
+ self.ssl_verify = True
80
111
  self.gl = gitlab.Gitlab(
81
- self.server, private_token=token, ssl_verify=ssl_verify, timeout=timeout
112
+ self.server,
113
+ private_token=token,
114
+ ssl_verify=self.ssl_verify,
115
+ timeout=timeout,
82
116
  )
83
117
  self._auth()
84
118
  self.user: CurrentUser = self.gl.user
@@ -88,11 +122,24 @@ class GitLabApi: # pylint: disable=too-many-public-methods
88
122
  if project_url is not None:
89
123
  parsed_project_url = urlparse(project_url)
90
124
  name_with_namespace = parsed_project_url.path.strip("/")
125
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
91
126
  self.project = self.gl.projects.get(name_with_namespace)
92
127
  else:
128
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
93
129
  self.project = self.gl.projects.get(project_id)
94
130
  self.saas_files = saas_files
95
131
 
132
+ @cached_property
133
+ def project_main_branch(self) -> str:
134
+ return next(
135
+ (b.name for b in self.project.branches.list(iterator=True) if b.default),
136
+ DEFAULT_MAIN_BRANCH,
137
+ )
138
+
139
+ @property
140
+ def main_branch(self) -> str:
141
+ return self.project_main_branch if self.project else DEFAULT_MAIN_BRANCH
142
+
96
143
  def __enter__(self):
97
144
  return self
98
145
 
@@ -127,13 +174,11 @@ class GitLabApi: # pylint: disable=too-many-public-methods
127
174
  """
128
175
 
129
176
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
130
- self.project.commits.create(
131
- {
132
- "branch": branch_name,
133
- "commit_message": commit_message,
134
- "actions": actions,
135
- }
136
- )
177
+ self.project.commits.create({
178
+ "branch": branch_name,
179
+ "commit_message": commit_message,
180
+ "actions": actions,
181
+ })
137
182
 
138
183
  def create_file(self, branch_name, file_path, commit_message, content):
139
184
  data = {
@@ -186,27 +231,23 @@ class GitLabApi: # pylint: disable=too-many-public-methods
186
231
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
187
232
  return self.project.mergerequests.create(data)
188
233
 
189
- def mr_exists(self, title):
234
+ def mr_exists(self, title: str) -> bool:
190
235
  mrs = self.get_merge_requests(state=MRState.OPENED)
191
- for mr in mrs:
192
- # since we are using a naming convention for these MRs
193
- # we can determine if a pending MR exists based on the title
194
- if mr.attributes.get("title") != title:
195
- continue
196
-
197
- return True
198
-
199
- return False
236
+ # since we are using a naming convention for these MRs
237
+ # we can determine if a pending MR exists based on the title
238
+ return any(mr.title == title for mr in mrs)
200
239
 
201
240
  @retry()
202
- def get_project_maintainers(self, repo_url=None):
203
- if repo_url is None:
204
- project = self.project
205
- else:
206
- project = self.get_project(repo_url)
241
+ def get_project_maintainers(
242
+ self, repo_url: str | None = None, query: dict | None = None
243
+ ) -> list[str] | None:
244
+ project = self.project if repo_url is None else self.get_project(repo_url)
207
245
  if project is None:
208
246
  return None
209
- members = self.get_items(project.members.all)
247
+ if query:
248
+ members = self.get_items(project.members_all.list, query_parameters=query)
249
+ else:
250
+ members = self.get_items(project.members_all.list)
210
251
  return [m.username for m in members if m.access_level >= 40]
211
252
 
212
253
  def get_app_sre_group_users(self):
@@ -214,26 +255,51 @@ class GitLabApi: # pylint: disable=too-many-public-methods
214
255
  app_sre_group = self.gl.groups.get("app-sre")
215
256
  return self.get_items(app_sre_group.members.list)
216
257
 
217
- def check_group_exists(self, group_name):
218
- groups = self.get_items(self.gl.groups.list)
219
- group_names = list(map(lambda x: x.name, groups))
220
- if group_name not in group_names:
221
- return False
222
- return True
258
+ def get_group_if_exists(self, group_name: str) -> Group | None:
259
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
260
+ try:
261
+ return self.gl.groups.get(group_name)
262
+ except gitlab.exceptions.GitlabGetError:
263
+ return None
223
264
 
224
- def get_group_members(self, group_name):
225
- if not self.check_group_exists(group_name):
226
- logging.error(group_name + " group not found")
227
- return []
265
+ def share_project_with_group(
266
+ self,
267
+ project: Project,
268
+ group_id: int,
269
+ access_level: int,
270
+ reshare: bool = False,
271
+ ) -> None:
272
+ if reshare:
273
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
274
+ project.unshare(group_id)
228
275
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
229
- group = self.gl.groups.get(group_name)
230
- return [
231
- {
232
- "user": m.username,
233
- "access_level": self.get_access_level_string(m.access_level),
234
- }
235
- for m in self.get_items(group.members.list)
236
- ]
276
+ project.share(group_id, access_level)
277
+
278
+ @staticmethod
279
+ def _is_bot_username(username: str) -> bool:
280
+ """crudely checking for the username
281
+
282
+ as gitlab-python require a major upgrade to use the billable members apis
283
+ https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#id11 lists the api
284
+ billable_membersis the attribute that provides billable members of groups
285
+
286
+ the second api is https://python-gitlab.readthedocs.io/en/stable/gl_objects/group_access_tokens.html
287
+ which provides a list of access tokens as well as their assigned users
288
+
289
+ those apis are not avaliable in python-gitlab v1.x
290
+ """
291
+ return GROUP_BOT_NAME_REGEX.match(username) is not None
292
+
293
+ def get_group_members(self, group: Group | None) -> list[GroupMember]:
294
+ if group is None:
295
+ logging.error("no group provided")
296
+ return []
297
+ else:
298
+ return [
299
+ m
300
+ for m in self.get_items(group.members.list)
301
+ if not self._is_bot_username(m.username)
302
+ ]
237
303
 
238
304
  def add_project_member(self, repo_url, user, access="maintainer"):
239
305
  project = self.get_project(repo_url)
@@ -247,74 +313,66 @@ class GitLabApi: # pylint: disable=too-many-public-methods
247
313
  member.access_level = access_level
248
314
  member.save()
249
315
 
250
- def add_group_member(self, group_name, username, access):
251
- if not self.check_group_exists(group_name):
252
- logging.error(group_name + " group not found")
253
- else:
316
+ def add_group_member(self, group, user):
317
+ gitlab_user = self.get_user(user.user)
318
+ if gitlab_user is not None:
254
319
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
255
- group = self.gl.groups.get(group_name)
256
- user = self.get_user(username)
257
- access_level = self.get_access_level(access)
258
- if user is not None:
320
+ try:
321
+ group.members.create({
322
+ "user_id": gitlab_user.id,
323
+ "access_level": user.access_level,
324
+ })
325
+ except gitlab.exceptions.GitlabCreateError:
259
326
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
260
- try:
261
- group.members.create(
262
- {"user_id": user.id, "access_level": access_level}
263
- )
264
- except gitlab.exceptions.GitlabCreateError:
265
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
266
- member = group.members.get(user.id)
267
- member.access_level = access_level
327
+ member = group.members.get(user.user)
328
+ member.access_level = user.access_level
329
+ member.save()
268
330
 
269
- def remove_group_member(self, group_name, username):
331
+ def remove_group_member(self, group, user_id):
270
332
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
271
- group = self.gl.groups.get(group_name)
272
- user = self.get_user(username)
273
- if user is not None:
274
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
275
- group.members.delete(user.id)
333
+ group.members.delete(user_id)
276
334
 
277
- def change_access(self, group, username, access):
278
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
279
- group = self.gl.groups.get(group)
280
- user = self.get_user(username)
335
+ def change_access(self, member, access_level):
336
+ member.access_level = access_level
281
337
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
282
- member = group.members.get(user.id)
283
- member.access_level = self.get_access_level(access)
284
338
  member.save()
285
339
 
286
340
  @staticmethod
287
341
  def get_access_level_string(access_level):
288
- if access_level == gitlab.OWNER_ACCESS:
342
+ if access_level == OWNER_ACCESS:
289
343
  return "owner"
290
- if access_level == gitlab.MAINTAINER_ACCESS:
344
+ if access_level == MAINTAINER_ACCESS:
291
345
  return "maintainer"
292
- if access_level == gitlab.DEVELOPER_ACCESS:
346
+ if access_level == DEVELOPER_ACCESS:
293
347
  return "developer"
294
- if access_level == gitlab.REPORTER_ACCESS:
348
+ if access_level == REPORTER_ACCESS:
295
349
  return "reporter"
296
- if access_level == gitlab.GUEST_ACCESS:
350
+ if access_level == GUEST_ACCESS:
297
351
  return "guest"
298
352
 
299
353
  @staticmethod
300
354
  def get_access_level(access):
301
355
  access = access.lower()
302
356
  if access == "owner":
303
- return gitlab.OWNER_ACCESS
357
+ return OWNER_ACCESS
304
358
  if access == "maintainer":
305
- return gitlab.MAINTAINER_ACCESS
359
+ return MAINTAINER_ACCESS
306
360
  if access == "developer":
307
- return gitlab.DEVELOPER_ACCESS
361
+ return DEVELOPER_ACCESS
308
362
  if access == "reporter":
309
- return gitlab.REPORTER_ACCESS
363
+ return REPORTER_ACCESS
310
364
  if access == "guest":
311
- return gitlab.GUEST_ACCESS
365
+ return GUEST_ACCESS
312
366
 
313
- def get_group_id_and_projects(self, group_name):
367
+ def get_group_id_and_projects(self, group_name: str) -> tuple[str, list[str]]:
314
368
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
315
369
  group = self.gl.groups.get(group_name)
316
370
  return group.id, [p.name for p in self.get_items(group.projects.list)]
317
371
 
372
+ def get_group(self, group_name: str) -> Group:
373
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
374
+ return self.gl.groups.get(group_name)
375
+
318
376
  def create_project(self, group_id, project):
319
377
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
320
378
  self.gl.projects.create({"name": project, "namespace_id": group_id})
@@ -323,7 +381,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
323
381
  return f"{self.server}/{group}/{project}"
324
382
 
325
383
  @retry()
326
- def get_project(self, repo_url):
384
+ def get_project(self, repo_url: str) -> Project | None:
327
385
  repo = repo_url.replace(self.server + "/", "")
328
386
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
329
387
  try:
@@ -347,14 +405,22 @@ class GitLabApi: # pylint: disable=too-many-public-methods
347
405
  return self.get_items(mr.resourcelabelevents.list)
348
406
 
349
407
  def get_merge_request_pipelines(self, mr: ProjectMergeRequest) -> list[dict]:
408
+ # TODO: use typed object in return
409
+ # TODO: use server side order_by
410
+ items = self.get_items(mr.pipelines.list)
350
411
  return sorted(
351
- self.get_items(mr.pipelines), key=lambda x: x["created_at"], reverse=True
412
+ [i.asdict() for i in items],
413
+ key=lambda x: x["created_at"],
414
+ reverse=True,
352
415
  )
353
416
 
354
- def get_merge_request_changed_paths(self, mr_id: int) -> list[str]:
417
+ @staticmethod
418
+ def get_merge_request_changed_paths(
419
+ merge_request: ProjectMergeRequest,
420
+ ) -> list[str]:
355
421
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
356
- merge_request = self.project.mergerequests.get(mr_id)
357
- changes = merge_request.changes()["changes"]
422
+ result = merge_request.changes()
423
+ changes = cast(dict, result)["changes"]
358
424
  changed_paths = set()
359
425
  for change in changes:
360
426
  old_path = change["old_path"]
@@ -363,101 +429,126 @@ class GitLabApi: # pylint: disable=too-many-public-methods
363
429
  changed_paths.add(new_path)
364
430
  return list(changed_paths)
365
431
 
366
- def get_merge_request_author_username(self, mr_id: int) -> str:
367
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
368
- merge_request = self.project.mergerequests.get(mr_id)
432
+ @staticmethod
433
+ def get_merge_request_author_username(
434
+ merge_request: ProjectMergeRequest,
435
+ ) -> str:
369
436
  return merge_request.author["username"]
370
437
 
438
+ @staticmethod
371
439
  def get_merge_request_comments(
372
- self, mr_id: int, include_description: bool = False
440
+ merge_request: ProjectMergeRequest,
441
+ include_description: bool = False,
373
442
  ) -> list[dict[str, Any]]:
374
443
  comments = []
375
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
376
- merge_request = self.project.mergerequests.get(mr_id)
377
444
  if include_description:
378
- comments.append(
379
- {
380
- "username": merge_request.author["username"],
381
- "body": merge_request.description,
382
- "created_at": merge_request.created_at,
383
- "id": MR_DESCRIPTION_COMMENT_ID,
384
- }
385
- )
386
- for note in self.get_items(merge_request.notes.list):
445
+ comments.append({
446
+ "username": merge_request.author["username"],
447
+ "body": merge_request.description,
448
+ "created_at": merge_request.created_at,
449
+ "id": MR_DESCRIPTION_COMMENT_ID,
450
+ })
451
+ for note in GitLabApi.get_items(merge_request.notes.list):
387
452
  if note.system:
388
453
  continue
389
- comments.append(
390
- {
391
- "username": note.author["username"],
392
- "body": note.body,
393
- "created_at": note.created_at,
394
- "id": note.id,
395
- }
396
- )
454
+ comments.append({
455
+ "username": note.author["username"],
456
+ "body": note.body,
457
+ "created_at": note.created_at,
458
+ "id": note.id,
459
+ "note": note,
460
+ })
397
461
  return comments
398
462
 
399
- def delete_gitlab_comment(self, mr_id, comment_id):
400
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
401
- merge_request = self.project.mergerequests.get(mr_id)
402
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
403
- note = merge_request.notes.get(comment_id)
463
+ @staticmethod
464
+ def delete_comment(note: ProjectMergeRequestNote) -> None:
404
465
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
405
466
  note.delete()
406
467
 
407
- def add_comment_on_merge_request(
408
- self, merge_request: ProjectMergeRequest, comment: str
468
+ def delete_merge_request_comments(
469
+ self,
470
+ merge_request: ProjectMergeRequest,
471
+ startswith: str,
409
472
  ) -> None:
410
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
411
- merge_request.notes.create({"body": comment})
473
+ comments = self.get_merge_request_comments(merge_request)
474
+ for c in comments:
475
+ body = c["body"] or ""
476
+ if c["username"] == self.user.username and body.startswith(startswith):
477
+ self.delete_comment(c["note"])
412
478
 
413
- def add_merge_request_comment(self, mr_id, comment):
414
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
415
- merge_request = self.project.mergerequests.get(mr_id)
416
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
417
- merge_request.notes.create({"body": comment})
418
-
419
- def get_project_labels(self):
420
- return [ln.name for ln in self.get_items(self.project.labels.list)]
479
+ @retry()
480
+ def get_project_labels(self) -> Set[str]:
481
+ return {ln.name for ln in self.get_items(self.project.labels.list)}
421
482
 
422
- def get_merge_request_labels(self, mr_id):
423
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
424
- merge_request = self.project.mergerequests.get(mr_id)
425
- return merge_request.labels
483
+ @staticmethod
484
+ def add_label_to_merge_request(
485
+ merge_request: ProjectMergeRequest,
486
+ label: str,
487
+ ) -> None:
488
+ # merge_request maybe stale, refresh it to reduce the possibility of labels overwriting
489
+ GitLabApi.refresh_labels(merge_request)
426
490
 
427
- def add_label_to_merge_request(self, mr_id, label):
428
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
429
- merge_request = self.project.mergerequests.get(mr_id)
430
- labels = merge_request.attributes.get("labels")
491
+ labels = merge_request.labels
492
+ if label in labels:
493
+ return
431
494
  labels.append(label)
432
- self.update_labels(merge_request, "merge-request", labels)
495
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
496
+ merge_request.save()
433
497
 
434
- def add_labels_to_merge_request(self, mr_id, labels):
498
+ @staticmethod
499
+ def add_labels_to_merge_request(
500
+ merge_request: ProjectMergeRequest,
501
+ labels: Iterable[str],
502
+ ):
435
503
  """Adds labels to a Merge Request"""
504
+ # merge_request maybe stale, refresh it to reduce the possibility of labels overwriting
505
+ GitLabApi.refresh_labels(merge_request)
506
+
507
+ new_labels = set(labels) - set(merge_request.labels)
508
+ if not new_labels:
509
+ return
510
+ merge_request.labels.extend(new_labels)
436
511
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
437
- merge_request = self.project.mergerequests.get(mr_id)
438
- mr_labels = merge_request.attributes.get("labels")
439
- mr_labels += labels
440
- self.update_labels(merge_request, "merge-request", mr_labels)
512
+ merge_request.save()
441
513
 
442
- def set_labels_on_merge_request(self, mr_id, labels):
514
+ @staticmethod
515
+ def set_labels_on_merge_request(
516
+ merge_request: ProjectMergeRequest,
517
+ labels: Iterable[str],
518
+ ) -> None:
443
519
  """Set labels to a Merge Request"""
444
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
445
- merge_request = self.project.mergerequests.get(mr_id)
446
- self.update_labels(merge_request, "merge-request", labels)
520
+ desired_labels = set(labels)
521
+ current_labels = set(merge_request.labels)
522
+ labels_to_add = desired_labels - current_labels
523
+ labels_to_remove = current_labels - desired_labels
524
+
525
+ if not labels_to_add and not labels_to_remove:
526
+ return
527
+
528
+ # merge_request maybe stale, refresh it to reduce the possibility of labels overwriting
529
+ GitLabApi.refresh_labels(merge_request)
530
+
531
+ refreshed_current_labels = set(merge_request.labels)
532
+ new_desired_labels = (
533
+ refreshed_current_labels - labels_to_remove
534
+ ) | labels_to_add
535
+
536
+ if new_desired_labels == refreshed_current_labels:
537
+ return
447
538
 
448
- def remove_label_from_merge_request(self, mr_id, label):
539
+ merge_request.labels = list(new_desired_labels)
449
540
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
450
- merge_request = self.project.mergerequests.get(mr_id)
451
- labels = merge_request.attributes.get("labels")
452
- if label in labels:
453
- labels.remove(label)
454
- self.update_labels(merge_request, "merge-request", labels)
541
+ merge_request.save()
455
542
 
456
- def add_comment_to_merge_request(self, mr_id, body):
543
+ @staticmethod
544
+ def add_comment_to_merge_request(
545
+ merge_request: ProjectMergeRequest,
546
+ body: str,
547
+ ) -> None:
457
548
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
458
- merge_request = self.project.mergerequests.get(mr_id)
459
549
  merge_request.notes.create({"body": body})
460
550
 
551
+ # TODO: deprecated this method as new support of list(get_all=True), and figure out request counter metrics
461
552
  @staticmethod
462
553
  def get_items(method, **kwargs):
463
554
  all_items = []
@@ -476,35 +567,75 @@ class GitLabApi: # pylint: disable=too-many-public-methods
476
567
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
477
568
  self.project.labels.create({"name": label_text, "color": label_color})
478
569
 
479
- def add_label(self, item, item_type, label):
480
- note_body = (
481
- "item has been marked as {0}. " "to remove say `/{0} cancel`"
482
- ).format(label)
483
- labels = item.attributes.get("labels")
570
+ @staticmethod
571
+ def refresh_labels(item: ProjectMergeRequest | ProjectIssue):
572
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
573
+ manager: ProjectMergeRequestManager | ProjectIssueManager
574
+ match item:
575
+ case ProjectMergeRequest():
576
+ manager = cast(ProjectMergeRequestManager, item.manager)
577
+ case ProjectIssue():
578
+ manager = cast(ProjectIssueManager, item.manager)
579
+ case _:
580
+ raise ValueError("item must be a ProjectMergeRequest or ProjectIssue")
581
+ item_id = item.get_id()
582
+ if item_id is None:
583
+ raise ValueError("item must have an id")
584
+ refreshed_item = manager.get(item_id)
585
+ item.labels = refreshed_item.labels
586
+
587
+ @staticmethod
588
+ def add_label_with_note(
589
+ item: ProjectMergeRequest | ProjectIssue,
590
+ label: str,
591
+ ) -> None:
592
+ # item maybe stale, refresh it to reduce the possibility of labels overwriting
593
+ GitLabApi.refresh_labels(item)
594
+
595
+ labels = item.labels
596
+ if label in labels:
597
+ return
484
598
  labels.append(label)
599
+ note_body = (
600
+ f"item has been marked as {label}. " f"to remove say `/{label} cancel`"
601
+ )
485
602
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
486
603
  item.notes.create({"body": note_body})
487
- self.update_labels(item, item_type, labels)
604
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
605
+ item.save()
488
606
 
489
- def remove_label(self, item, item_type, label):
490
- labels = item.attributes.get("labels")
607
+ @staticmethod
608
+ def remove_label(
609
+ item: ProjectMergeRequest | ProjectIssue,
610
+ label: str,
611
+ ):
612
+ # item maybe stale, refresh it to reduce the possibility of labels overwriting
613
+ GitLabApi.refresh_labels(item)
614
+
615
+ labels = item.labels
616
+ if label not in labels:
617
+ return
491
618
  labels.remove(label)
492
- self.update_labels(item, item_type, labels)
619
+ gitlab_request.labels(integration=INTEGRATION_NAME).inc()
620
+ item.save()
621
+
622
+ @staticmethod
623
+ def remove_labels(
624
+ item: ProjectMergeRequest | ProjectIssue,
625
+ labels: Iterable[str],
626
+ ):
627
+ # item maybe stale, refresh it to reduce the possibility of labels overwriting
628
+ GitLabApi.refresh_labels(item)
629
+
630
+ current_labels = set(item.labels)
631
+ to_be_removed = set(labels) & current_labels
632
+
633
+ if not to_be_removed:
634
+ return
635
+ item.labels = list(current_labels - to_be_removed)
493
636
 
494
- def update_labels(self, item, item_type, labels):
495
- if item_type == "issue":
496
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
497
- editable_item = self.project.issues.get(
498
- item.attributes.get("iid"), lazy=True
499
- )
500
- elif item_type == "merge-request":
501
- gitlab_request.labels(integration=INTEGRATION_NAME).inc()
502
- editable_item = self.project.mergerequests.get(
503
- item.attributes.get("iid"), lazy=True
504
- )
505
- editable_item.labels = labels
506
637
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
507
- editable_item.save()
638
+ item.save()
508
639
 
509
640
  @staticmethod
510
641
  def close(item):
@@ -514,7 +645,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
514
645
 
515
646
  def get_user(self, username):
516
647
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
517
- user = self.gl.users.list(search=username)
648
+ user = self.gl.users.list(search=username, page=1, per_page=1)
518
649
  if len(user) == 0:
519
650
  logging.error(username + " user not found")
520
651
  return
@@ -527,7 +658,8 @@ class GitLabApi: # pylint: disable=too-many-public-methods
527
658
  if p is None:
528
659
  return []
529
660
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
530
- return p.hooks.list(per_page=100)
661
+ # TODO: get_all may send multiple requests, update metrics accordingly
662
+ return p.hooks.list(per_page=100, get_all=True)
531
663
 
532
664
  def create_project_hook(self, repo_url, data):
533
665
  p = self.get_project(repo_url)
@@ -538,9 +670,9 @@ class GitLabApi: # pylint: disable=too-many-public-methods
538
670
  hook = {
539
671
  "url": url,
540
672
  "enable_ssl_verification": 1,
541
- "note_events": int(trigger == "mr"),
542
- "push_events": int(trigger == "push"),
543
- "merge_requests_events": int(trigger == "mr"),
673
+ "note_events": int("note" in trigger),
674
+ "push_events": int("push" in trigger),
675
+ "merge_requests_events": int("mr" in trigger),
544
676
  }
545
677
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
546
678
  p.hooks.create(hook)
@@ -582,7 +714,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
582
714
  # what is the time of the last app-sre response?
583
715
  last_action_by_team = None
584
716
  # comments
585
- comments = self.get_merge_request_comments(mr.iid)
717
+ comments = self.get_merge_request_comments(mr)
586
718
  comments.sort(key=itemgetter("created_at"), reverse=True)
587
719
  for comment in comments:
588
720
  username = comment["username"]
@@ -593,7 +725,8 @@ class GitLabApi: # pylint: disable=too-many-public-methods
593
725
  break
594
726
  # labels
595
727
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
596
- label_events = mr.resourcelabelevents.list()
728
+ # TODO: this may send multiple requests, update metrics accordingly
729
+ label_events = mr.resourcelabelevents.list(get_all=True)
597
730
  for label in reversed(label_events):
598
731
  if label.action == "add" and label.label["name"] in hold_labels:
599
732
  username = label.user["username"]
@@ -642,7 +775,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
642
775
  author, assignee = last_assignment[0], last_assignment[1]
643
776
  return author in team_usernames and mr.assignee["username"] == assignee
644
777
 
645
- def last_assignment(self, mr: ProjectMergeRequest) -> Optional[tuple[str, str]]:
778
+ def last_assignment(self, mr: ProjectMergeRequest) -> tuple[str, str] | None:
646
779
  body_format = "assigned to @"
647
780
  notes = self.get_items(mr.notes.list)
648
781
 
@@ -661,8 +794,8 @@ class GitLabApi: # pylint: disable=too-many-public-methods
661
794
 
662
795
  def last_comment(
663
796
  self, mr: ProjectMergeRequest, exclude_bot=True
664
- ) -> Optional[dict[str, Any]]:
665
- comments = self.get_merge_request_comments(mr.iid)
797
+ ) -> dict[str, Any] | None:
798
+ comments = self.get_merge_request_comments(mr)
666
799
  comments.sort(key=itemgetter("created_at"), reverse=True)
667
800
  for comment in comments:
668
801
  username = comment["username"]
@@ -674,5 +807,15 @@ class GitLabApi: # pylint: disable=too-many-public-methods
674
807
  def get_commit_sha(self, ref: str, repo_url: str) -> str:
675
808
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
676
809
  project = self.get_project(repo_url)
677
- commits = project.commits.list(ref_name=ref, per_page=1)
810
+ commits = project.commits.list(ref_name=ref, per_page=1, page=1)
678
811
  return commits[0].id
812
+
813
+ def repository_compare(
814
+ self, repo_url: str, ref_from: str, ref_to: str
815
+ ) -> list[dict[str, Any]]:
816
+ project = self.get_project(repo_url)
817
+ response: Any = project.repository_compare(ref_from, ref_to)
818
+ return response.get("commits", [])
819
+
820
+ def get_personal_access_tokens(self) -> list[PersonalAccessToken]:
821
+ return self.get_items(self.gl.personal_access_tokens.list)