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,275 @@
1
+ import logging
2
+ from abc import (
3
+ ABC,
4
+ abstractmethod,
5
+ )
6
+ from collections.abc import Iterable, Mapping
7
+ from typing import (
8
+ Any,
9
+ Optional,
10
+ )
11
+
12
+ from pydantic import BaseModel
13
+
14
+ from reconcile.gql_definitions.status_board.status_board import StatusBoardV1
15
+ from reconcile.typed_queries.status_board import (
16
+ get_selected_app_names,
17
+ get_status_board,
18
+ )
19
+ from reconcile.utils.differ import diff_mappings
20
+ from reconcile.utils.ocm.status_board import (
21
+ create_application,
22
+ create_product,
23
+ delete_application,
24
+ delete_product,
25
+ get_managed_products,
26
+ get_product_applications,
27
+ )
28
+ from reconcile.utils.ocm_base_client import (
29
+ OCMBaseClient,
30
+ init_ocm_base_client,
31
+ )
32
+ from reconcile.utils.runtime.integration import QontractReconcileIntegration
33
+
34
+ QONTRACT_INTEGRATION = "status-board-exporter"
35
+
36
+
37
+ class AbstractStatusBoard(ABC, BaseModel):
38
+ """Abstract class for upgrade policies
39
+ Used to create and delete upgrade policies in OCM."""
40
+
41
+ id: str | None
42
+ name: str
43
+ fullname: str
44
+ metadata: dict[str, Any] | None
45
+
46
+ @abstractmethod
47
+ def create(self, ocm: OCMBaseClient) -> None:
48
+ pass
49
+
50
+ @abstractmethod
51
+ def delete(self, ocm: OCMBaseClient) -> None:
52
+ pass
53
+
54
+ @abstractmethod
55
+ def summarize(self) -> str:
56
+ pass
57
+
58
+ @staticmethod
59
+ @abstractmethod
60
+ def get_priority() -> int:
61
+ pass
62
+
63
+
64
+ class Product(AbstractStatusBoard):
65
+ applications: list["Application"] | None
66
+
67
+ def create(self, ocm: OCMBaseClient) -> None:
68
+ spec = self.dict(by_alias=True)
69
+ spec.pop("applications")
70
+ spec.pop("id")
71
+ self.id = create_product(ocm, spec)
72
+
73
+ def delete(self, ocm: OCMBaseClient) -> None:
74
+ if not self.id:
75
+ logging.error(f'Trying to delete Product "{self.name}" without id')
76
+ return
77
+ delete_product(ocm, self.id)
78
+
79
+ def summarize(self) -> str:
80
+ return f'Product: "{self.name}"'
81
+
82
+ @staticmethod
83
+ def get_priority() -> int:
84
+ return 0
85
+
86
+
87
+ class Application(AbstractStatusBoard):
88
+ product: Optional["Product"]
89
+
90
+ def create(self, ocm: OCMBaseClient) -> None:
91
+ spec = self.dict(by_alias=True)
92
+ spec.pop("id")
93
+ product = spec.pop("product")
94
+ product_id = product.get("id")
95
+ if product_id:
96
+ spec["product"] = {"id": product_id}
97
+ self.id = create_application(ocm, spec)
98
+ else:
99
+ logging.warning("Missing product id for application")
100
+
101
+ def delete(self, ocm: OCMBaseClient) -> None:
102
+ if not self.id:
103
+ logging.error(f'Trying to delete Application "{self.name}" without id')
104
+ return
105
+ delete_application(ocm, self.id)
106
+
107
+ def summarize(self) -> str:
108
+ return f'Application: "{self.name}" "{self.fullname}"'
109
+
110
+ @staticmethod
111
+ def get_priority() -> int:
112
+ return 1
113
+
114
+
115
+ class StatusBoardHandler(BaseModel):
116
+ action: str
117
+ status_board_object: AbstractStatusBoard
118
+
119
+ def act(self, dry_run: bool, ocm: OCMBaseClient) -> None:
120
+ logging.info(f"{self.action} - {self.status_board_object.summarize()}")
121
+ if dry_run:
122
+ return
123
+
124
+ match self.action:
125
+ case "delete":
126
+ self.status_board_object.delete(ocm)
127
+ case "create":
128
+ self.status_board_object.create(ocm)
129
+
130
+
131
+ class StatusBoardExporterIntegration(QontractReconcileIntegration):
132
+ @property
133
+ def name(self) -> str:
134
+ return QONTRACT_INTEGRATION
135
+
136
+ @staticmethod
137
+ def get_product_apps(sb: StatusBoardV1) -> dict[str, set[str]]:
138
+ global_selectors = (
139
+ sb.global_app_selectors.exclude or [] if sb.global_app_selectors else []
140
+ )
141
+ return {
142
+ p.product_environment.product.name: get_selected_app_names(
143
+ global_selectors, p
144
+ )
145
+ for p in sb.products
146
+ }
147
+
148
+ @staticmethod
149
+ def get_current_products_applications(ocm_api: OCMBaseClient) -> list[Product]:
150
+ products_raw = get_managed_products(ocm_api)
151
+ products = [Product(**p) for p in products_raw]
152
+
153
+ for p in products:
154
+ if not p.id:
155
+ logging.error(f'Product "{p.name}" has no id')
156
+ continue
157
+ p.applications = [
158
+ Application(**a) for a in get_product_applications(ocm_api, p.id)
159
+ ]
160
+
161
+ return products
162
+
163
+ @staticmethod
164
+ def get_diff(
165
+ desired_product_apps: Mapping[str, set[str]],
166
+ current_products_applications: Iterable[Product],
167
+ ) -> list[StatusBoardHandler]:
168
+ def create_app(app_name: str, product: Product) -> Application:
169
+ return Application(
170
+ name=app_name,
171
+ fullname=f"{product.name}/{app_name}",
172
+ product=product,
173
+ )
174
+
175
+ return_list: list[StatusBoardHandler] = []
176
+ current_products = {p.name: p for p in current_products_applications}
177
+
178
+ current_as_mapping: Mapping[str, set[str]] = {
179
+ c.name: {a.name for a in c.applications or []}
180
+ for c in current_products_applications
181
+ }
182
+
183
+ diff_result = diff_mappings(
184
+ current_as_mapping,
185
+ desired_product_apps,
186
+ )
187
+
188
+ for product_name in diff_result.add:
189
+ product = Product(name=product_name, fullname=product_name, applications=[])
190
+ return_list.append(
191
+ StatusBoardHandler(
192
+ action="create",
193
+ status_board_object=product,
194
+ )
195
+ )
196
+ # new product, so it misses also the applications
197
+ for app_name in desired_product_apps[product_name]:
198
+ return_list.append(
199
+ StatusBoardHandler(
200
+ action="create",
201
+ status_board_object=create_app(app_name, product),
202
+ )
203
+ )
204
+
205
+ # existing product, only add/remove applications
206
+ for product_name, apps in diff_result.change.items():
207
+ product = current_products[product_name]
208
+ for app_name in apps.desired - apps.current:
209
+ return_list.append(
210
+ StatusBoardHandler(
211
+ action="create",
212
+ status_board_object=create_app(app_name, product),
213
+ )
214
+ )
215
+ to_delete = apps.current - apps.desired
216
+ for application in product.applications or []:
217
+ if application.name in to_delete:
218
+ return_list.append(
219
+ StatusBoardHandler(
220
+ action="delete",
221
+ status_board_object=application,
222
+ )
223
+ )
224
+
225
+ # product is deleted entirely
226
+ for product_name in diff_result.delete:
227
+ for application in current_products[product_name].applications or []:
228
+ return_list.append(
229
+ StatusBoardHandler(action="delete", status_board_object=application)
230
+ )
231
+ return_list.append(
232
+ StatusBoardHandler(
233
+ action="delete", status_board_object=current_products[product_name]
234
+ )
235
+ )
236
+ return return_list
237
+
238
+ @staticmethod
239
+ def apply_diff(
240
+ dry_run: bool, ocm_api: OCMBaseClient, diff: list[StatusBoardHandler]
241
+ ) -> None:
242
+ creations: list[StatusBoardHandler] = []
243
+ deletions: list[StatusBoardHandler] = []
244
+
245
+ for o in diff:
246
+ match o.action:
247
+ case "create":
248
+ creations.append(o)
249
+ case "delete":
250
+ deletions.append(o)
251
+
252
+ # Products need to be created before Applications
253
+ creations.sort(key=lambda x: x.status_board_object.get_priority())
254
+
255
+ # Applications need to be deleted before Products
256
+ deletions.sort(key=lambda x: x.status_board_object.get_priority(), reverse=True)
257
+
258
+ for d in creations + deletions:
259
+ d.act(dry_run, ocm_api)
260
+
261
+ def run(self, dry_run: bool) -> None:
262
+ # update cyclic reference
263
+ Product.update_forward_refs()
264
+
265
+ for sb in get_status_board():
266
+ ocm_api = init_ocm_base_client(sb.ocm, self.secret_reader)
267
+ desired_product_apps: dict[str, set[str]] = self.get_product_apps(sb)
268
+
269
+ current_products_applications = self.get_current_products_applications(
270
+ ocm_api
271
+ )
272
+
273
+ diff = self.get_diff(desired_product_apps, current_products_applications)
274
+
275
+ self.apply_diff(dry_run, ocm_api, diff)
@@ -1,5 +0,0 @@
1
- from reconcile.statuspage import atlassian
2
- from reconcile.statuspage.page import register_provider
3
-
4
- # Status page providers are registered here to prevent all kinds of cyclic imports.
5
- register_provider(atlassian.PROVIDER_NAME, atlassian.init_provider_for_page)
@@ -1,24 +1,26 @@
1
1
  import logging
2
+ import time
3
+ from datetime import datetime
2
4
  from typing import (
3
5
  Any,
4
- Optional,
5
- Protocol,
6
+ Self,
6
7
  )
7
8
 
8
- import statuspageio # type: ignore
9
+ import requests
9
10
  from pydantic import BaseModel
11
+ from requests import Response
12
+ from sretoolbox.utils import retry
10
13
 
11
14
  from reconcile.gql_definitions.statuspage.statuspages import StatusPageV1
12
15
  from reconcile.statuspage.page import (
13
16
  StatusComponent,
17
+ StatusMaintenance,
18
+ StatusMaintenanceAnnouncement,
14
19
  StatusPage,
15
- StatusPageProvider,
16
20
  )
17
21
  from reconcile.statuspage.state import ComponentBindingState
18
22
  from reconcile.statuspage.status import ManualStatusProvider
19
23
 
20
- PROVIDER_NAME = "atlassian"
21
-
22
24
 
23
25
  class AtlassianRawComponent(BaseModel):
24
26
  """
@@ -27,66 +29,118 @@ class AtlassianRawComponent(BaseModel):
27
29
 
28
30
  id: str
29
31
  name: str
30
- description: Optional[str]
32
+ description: str | None
31
33
  position: int
32
34
  status: str
33
- automation_email: Optional[str]
34
- group_id: Optional[str]
35
- group: Optional[bool]
35
+ automation_email: str | None
36
+ group_id: str | None
37
+ group: bool | None
36
38
 
37
39
 
38
- class AtlassianAPI(Protocol):
39
- def list_components(self) -> list[AtlassianRawComponent]:
40
- ...
40
+ class AtlassianRawMaintenanceUpdate(BaseModel):
41
+ """
42
+ atlassian status page REST schema for maintenance updates
43
+ """
41
44
 
42
- def update_component(self, id: str, data: dict[str, Any]) -> None:
43
- ...
45
+ body: str
44
46
 
45
- def create_component(self, data: dict[str, Any]) -> str:
46
- ...
47
47
 
48
- def delete_component(self, id: str) -> None:
49
- ...
48
+ class AtlassianRawMaintenance(BaseModel):
49
+ """
50
+ atlassian status page REST schema for maintenance
51
+ """
50
52
 
53
+ id: str
54
+ name: str
55
+ scheduled_for: str
56
+ scheduled_until: str
57
+ incident_updates: list[AtlassianRawMaintenanceUpdate]
58
+ components: list[AtlassianRawComponent]
59
+ auto_transition_deliver_notifications_at_end: bool | None
60
+ auto_transition_deliver_notifications_at_start: bool | None
61
+ scheduled_remind_prior: bool | None
51
62
 
52
- class LegacyLibAtlassianAPI:
63
+
64
+ class AtlassianAPI:
53
65
  """
54
- This API class wraps the statuspageio python library for basic component operations.
55
- This class is named Legacy for a couple reasons:
56
- * the underlying library is not maintained anymore
57
- * the library has no type annotated
58
- * the library does not support pagination which becomes important for this API
59
- Therefore this lib will be replaced by a think wrapper based on uplink in an upcoming PR.
66
+ This API class wraps the statuspageio REST API for basic component operations.
60
67
  """
61
68
 
62
69
  def __init__(self, page_id: str, api_url: str, token: str):
63
70
  self.page_id = page_id
64
71
  self.api_url = api_url
65
72
  self.token = token
66
- self._client = statuspageio.Client(
67
- api_key=self.token, page_id=self.page_id, organization_id="unset"
73
+ self.auth_headers = {"Authorization": f"OAuth {self.token}"}
74
+
75
+ @retry(max_attempts=10)
76
+ def _do_get(self, url: str, params: dict[str, Any]) -> Response:
77
+ response = requests.get(
78
+ url, params=params, headers=self.auth_headers, timeout=30
68
79
  )
80
+ response.raise_for_status()
81
+ return response
82
+
83
+ def _list_items(self, url: str) -> list[Any]:
84
+ all_items: list[Any] = []
85
+ page = 1
86
+ per_page = 100
87
+ while True:
88
+ params = {"page": page, "per_page": per_page}
89
+ response = self._do_get(url, params=params)
90
+ items = response.json()
91
+ all_items += items
92
+ if len(items) < per_page:
93
+ break
94
+ page += 1
95
+ # https://developer.statuspage.io/#section/Rate-Limiting
96
+ # Each API token is limited to 1 request / second as measured on a 60 second rolling window
97
+ time.sleep(1)
98
+
99
+ return all_items
69
100
 
70
101
  def list_components(self) -> list[AtlassianRawComponent]:
71
- return [
72
- AtlassianRawComponent(
73
- **c.toDict(),
74
- )
75
- for c in self._client.components.list()
76
- ]
102
+ url = f"{self.api_url}/v1/pages/{self.page_id}/components"
103
+ all_components = self._list_items(url)
104
+ return [AtlassianRawComponent(**c) for c in all_components]
77
105
 
78
106
  def update_component(self, id: str, data: dict[str, Any]) -> None:
79
- self._client.components.update(id, **data)
107
+ url = f"{self.api_url}/v1/pages/{self.page_id}/components/{id}"
108
+ requests.patch(
109
+ url, json={"component": data}, headers=self.auth_headers
110
+ ).raise_for_status()
80
111
 
81
112
  def create_component(self, data: dict[str, Any]) -> str:
82
- result = self._client.components.create(**data)
83
- return result["id"]
113
+ url = f"{self.api_url}/v1/pages/{self.page_id}/components"
114
+ response = requests.post(
115
+ url, json={"component": data}, headers=self.auth_headers
116
+ )
117
+ response.raise_for_status()
118
+ return response.json()["id"]
84
119
 
85
120
  def delete_component(self, id: str) -> None:
86
- self._client.components.delete(id)
121
+ url = f"{self.api_url}/v1/pages/{self.page_id}/components/{id}"
122
+ requests.delete(url, headers=self.auth_headers).raise_for_status()
123
+
124
+ def list_scheduled_maintenances(self) -> list[AtlassianRawMaintenance]:
125
+ url = f"{self.api_url}/v1/pages/{self.page_id}/incidents/scheduled"
126
+ all_scheduled_incidents = self._list_items(url)
127
+ return [AtlassianRawMaintenance(**i) for i in all_scheduled_incidents]
128
+
129
+ def list_active_maintenances(self) -> list[AtlassianRawMaintenance]:
130
+ url = f"{self.api_url}/v1/pages/{self.page_id}/incidents/active_maintenance"
131
+ all_active_incidents = self._list_items(url)
132
+ return [AtlassianRawMaintenance(**i) for i in all_active_incidents]
133
+
134
+ def create_incident(self, data: dict[str, Any]) -> str:
135
+ url = f"{self.api_url}/v1/pages/{self.page_id}/incidents"
136
+ response = requests.post(
137
+ url, json={"incident": data}, headers=self.auth_headers
138
+ )
139
+ response.raise_for_status()
140
+ return response.json()["id"]
87
141
 
88
142
 
89
- class AtlassianStatusPageProvider(StatusPageProvider):
143
+ class AtlassianStatusPageProvider:
90
144
  """
91
145
  The provider implements CRUD operations for Atlassian status pages.
92
146
  It also takes care of a mixed set of components on a page, where some
@@ -123,13 +177,13 @@ class AtlassianStatusPageProvider(StatusPageProvider):
123
177
  self._group_name_to_id = {g.name: g.id for g in self._components if g.group}
124
178
  self._group_id_to_name = {g.id: g.name for g in self._components if g.group}
125
179
 
126
- def get_component_by_id(self, id: str) -> Optional[StatusComponent]:
180
+ def get_component_by_id(self, id: str) -> StatusComponent | None:
127
181
  raw = self.get_raw_component_by_id(id)
128
182
  if raw:
129
183
  return self._bound_raw_component_to_status_component(raw)
130
184
  return None
131
185
 
132
- def get_raw_component_by_id(self, id: str) -> Optional[AtlassianRawComponent]:
186
+ def get_raw_component_by_id(self, id: str) -> AtlassianRawComponent | None:
133
187
  return self._components_by_id.get(id)
134
188
 
135
189
  def get_current_page(self) -> StatusPage:
@@ -146,34 +200,41 @@ class AtlassianStatusPageProvider(StatusPageProvider):
146
200
  components=[c for c in components if c is not None],
147
201
  )
148
202
 
203
+ def _raw_component_to_status_component(
204
+ self, raw_component: AtlassianRawComponent, name_override: str | None = None
205
+ ) -> StatusComponent:
206
+ group_name = (
207
+ self._group_id_to_name.get(raw_component.group_id)
208
+ if raw_component.group_id
209
+ else None
210
+ )
211
+ return StatusComponent(
212
+ name=name_override or raw_component.name,
213
+ display_name=raw_component.name,
214
+ description=raw_component.description,
215
+ group_name=group_name,
216
+ status_provider_configs=[
217
+ ManualStatusProvider(
218
+ component_status=raw_component.status,
219
+ )
220
+ ],
221
+ )
222
+
149
223
  def _bound_raw_component_to_status_component(
150
224
  self, raw_component: AtlassianRawComponent
151
- ) -> Optional[StatusComponent]:
225
+ ) -> StatusComponent | None:
152
226
  bound_component_name = self._binding_state.get_name_for_component_id(
153
227
  raw_component.id
154
228
  )
155
229
  if bound_component_name:
156
- group_name = (
157
- self._group_id_to_name.get(raw_component.group_id)
158
- if raw_component.group_id
159
- else None
160
- )
161
- return StatusComponent(
162
- name=bound_component_name,
163
- display_name=raw_component.name,
164
- description=raw_component.description,
165
- group_name=group_name,
166
- status_provider_configs=[
167
- ManualStatusProvider(
168
- component_status=raw_component.status,
169
- )
170
- ],
230
+ return self._raw_component_to_status_component(
231
+ raw_component, name_override=bound_component_name
171
232
  )
172
233
  return None
173
234
 
174
235
  def lookup_component(
175
236
  self, desired_component: StatusComponent
176
- ) -> tuple[Optional[AtlassianRawComponent], bool]:
237
+ ) -> tuple[AtlassianRawComponent | None, bool]:
177
238
  """
178
239
  Finds the component on the page that matches the desired component. This
179
240
  is either done explicitely by using binding information if available or
@@ -207,7 +268,7 @@ class AtlassianStatusPageProvider(StatusPageProvider):
207
268
  return component, bound
208
269
 
209
270
  def should_apply(
210
- self, desired: StatusComponent, current: Optional[AtlassianRawComponent]
271
+ self, desired: StatusComponent, current: AtlassianRawComponent | None
211
272
  ) -> bool:
212
273
  """
213
274
  Verifies if the desired component should be applied to the status page
@@ -240,8 +301,15 @@ class AtlassianStatusPageProvider(StatusPageProvider):
240
301
 
241
302
  # component status
242
303
  desired_component_status = desired.desired_component_status()
243
- status_update_required = desired_component_status is not None and (
244
- not current or desired_component_status != current.status
304
+ active_maintenance_affecting_component = [
305
+ m
306
+ for m in self.active_maintenances
307
+ if desired.display_name in [c.name for c in m.components]
308
+ ]
309
+ status_update_required = (
310
+ desired_component_status is not None
311
+ and (not current or desired_component_status != current.status)
312
+ and not active_maintenance_affecting_component
245
313
  )
246
314
 
247
315
  # shortcut execution if there is nothing to do
@@ -265,7 +333,7 @@ class AtlassianStatusPageProvider(StatusPageProvider):
265
333
  component_id=current_component.id,
266
334
  )
267
335
 
268
- # validte the component and check if the current state needs to be updated
336
+ # validate the component and check if the current state needs to be updated
269
337
  needs_update = self.should_apply(desired, current_component)
270
338
  if not needs_update:
271
339
  return
@@ -288,7 +356,13 @@ class AtlassianStatusPageProvider(StatusPageProvider):
288
356
  # resolve status
289
357
  desired_component_status = desired.desired_component_status()
290
358
  if desired_component_status:
291
- component_update["status"] = desired_component_status
359
+ active_maintenance_affecting_component = [
360
+ m
361
+ for m in self.active_maintenances
362
+ if desired.display_name in [c.name for c in m.components]
363
+ ]
364
+ if not active_maintenance_affecting_component:
365
+ component_update["status"] = desired_component_status
292
366
 
293
367
  if current_component:
294
368
  logging.info(f"update component {desired.name}: {component_update}")
@@ -333,21 +407,86 @@ class AtlassianStatusPageProvider(StatusPageProvider):
333
407
  if not dry_run:
334
408
  self._binding_state.bind_component(component_name, component_id)
335
409
 
410
+ def _raw_maintenance_to_status_maintenance(
411
+ self,
412
+ raw_maintenance: AtlassianRawMaintenance,
413
+ name_override: str | None = None,
414
+ ) -> StatusMaintenance:
415
+ return StatusMaintenance(
416
+ name=name_override or raw_maintenance.name,
417
+ message=raw_maintenance.incident_updates[0].body,
418
+ schedule_start=datetime.fromisoformat(raw_maintenance.scheduled_for),
419
+ schedule_end=datetime.fromisoformat(raw_maintenance.scheduled_until),
420
+ components=[
421
+ self._raw_component_to_status_component(c)
422
+ for c in raw_maintenance.components
423
+ ],
424
+ announcements=StatusMaintenanceAnnouncement(
425
+ remind_subscribers=raw_maintenance.scheduled_remind_prior,
426
+ notify_subscribers_on_start=raw_maintenance.auto_transition_deliver_notifications_at_start,
427
+ notify_subscribers_on_completion=raw_maintenance.auto_transition_deliver_notifications_at_end,
428
+ ),
429
+ )
336
430
 
337
- def init_provider_for_page(
338
- page: StatusPageV1,
339
- token: str,
340
- component_binding_state: ComponentBindingState,
341
- ) -> AtlassianStatusPageProvider:
342
- """
343
- Initializes the provider for atlassian status page.
344
- """
345
- return AtlassianStatusPageProvider(
346
- page_name=page.name,
347
- api=LegacyLibAtlassianAPI(
348
- page_id=page.page_id,
349
- api_url=page.api_url,
350
- token=token,
351
- ),
352
- component_binding_state=component_binding_state,
353
- )
431
+ @classmethod
432
+ def init_from_page(
433
+ cls,
434
+ page: StatusPageV1,
435
+ token: str,
436
+ component_binding_state: ComponentBindingState,
437
+ ) -> Self:
438
+ """
439
+ Initializes the provider for atlassian status page.
440
+ """
441
+ return cls(
442
+ page_name=page.name,
443
+ api=AtlassianAPI(
444
+ page_id=page.page_id,
445
+ api_url=page.api_url,
446
+ token=token,
447
+ ),
448
+ component_binding_state=component_binding_state,
449
+ )
450
+
451
+ @property
452
+ def scheduled_maintenances(self) -> list[StatusMaintenance]:
453
+ return [
454
+ self._raw_maintenance_to_status_maintenance(m)
455
+ for m in self._api.list_scheduled_maintenances()
456
+ ]
457
+
458
+ @property
459
+ def active_maintenances(self) -> list[StatusMaintenance]:
460
+ return [
461
+ self._raw_maintenance_to_status_maintenance(m)
462
+ for m in self._api.list_active_maintenances()
463
+ ]
464
+
465
+ def create_maintenance(self, maintenance: StatusMaintenance) -> None:
466
+ component_ids: list[str] = []
467
+ for sc in maintenance.components:
468
+ current_component, _ = self.lookup_component(sc)
469
+ if current_component:
470
+ component_ids.append(current_component.id)
471
+ data = {
472
+ "name": maintenance.name,
473
+ "status": "scheduled",
474
+ "scheduled_for": maintenance.schedule_start.isoformat(),
475
+ "scheduled_until": maintenance.schedule_end.isoformat(),
476
+ "body": maintenance.message,
477
+ "scheduled_remind_prior": maintenance.announcements.remind_subscribers,
478
+ "scheduled_auto_transition": True,
479
+ "scheduled_auto_in_progress": True,
480
+ "scheduled_auto_completed": True,
481
+ "component_ids": component_ids,
482
+ "auto_transition_to_maintenance_state": True,
483
+ "auto_transition_to_operational_state": True,
484
+ "auto_transition_deliver_notifications_at_start": maintenance.announcements.notify_subscribers_on_start,
485
+ "auto_transition_deliver_notifications_at_end": maintenance.announcements.notify_subscribers_on_completion,
486
+ }
487
+ incident_id = self._api.create_incident(data)
488
+ self._bind_component(
489
+ dry_run=False,
490
+ component_name=maintenance.name,
491
+ component_id=incident_id,
492
+ )