qontract-reconcile 0.10.1rc884__py3-none-any.whl → 0.10.1rc886__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 (279) hide show
  1. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/METADATA +1 -1
  2. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/RECORD +279 -276
  3. reconcile/acs_rbac.py +1 -2
  4. reconcile/aus/advanced_upgrade_service.py +14 -14
  5. reconcile/aus/aus_label_source.py +1 -2
  6. reconcile/aus/base.py +23 -26
  7. reconcile/aus/cluster_version_data.py +4 -4
  8. reconcile/aus/models.py +2 -3
  9. reconcile/aus/version_gate_approver.py +2 -6
  10. reconcile/aus/version_gates/__init__.py +1 -3
  11. reconcile/aus/version_gates/sts_version_gate_handler.py +2 -3
  12. reconcile/aws_account_manager/integration.py +2 -2
  13. reconcile/aws_ami_cleanup/integration.py +3 -4
  14. reconcile/aws_iam_password_reset.py +2 -5
  15. reconcile/aws_version_sync/integration.py +2 -2
  16. reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
  17. reconcile/change_owners/approver.py +4 -5
  18. reconcile/change_owners/bundle.py +20 -22
  19. reconcile/change_owners/change_types.py +23 -24
  20. reconcile/change_owners/changes.py +13 -16
  21. reconcile/change_owners/decision.py +2 -5
  22. reconcile/change_owners/diff.py +11 -15
  23. reconcile/change_owners/self_service_roles.py +1 -2
  24. reconcile/change_owners/tester.py +7 -10
  25. reconcile/checkpoint.py +2 -5
  26. reconcile/cli.py +9 -12
  27. reconcile/closedbox_endpoint_monitoring_base.py +8 -11
  28. reconcile/cluster_deployment_mapper.py +2 -5
  29. reconcile/cna/assets/asset.py +4 -7
  30. reconcile/cna/assets/null.py +2 -5
  31. reconcile/cna/integration.py +2 -3
  32. reconcile/cna/state.py +2 -5
  33. reconcile/dashdotdb_base.py +8 -11
  34. reconcile/dashdotdb_cso.py +3 -6
  35. reconcile/dashdotdb_dora.py +10 -14
  36. reconcile/dashdotdb_dvo.py +10 -13
  37. reconcile/dashdotdb_slo.py +5 -8
  38. reconcile/database_access_manager.py +5 -6
  39. reconcile/dynatrace_token_provider/integration.py +2 -5
  40. reconcile/external_resources/integration.py +1 -1
  41. reconcile/external_resources/manager.py +4 -4
  42. reconcile/external_resources/model.py +3 -3
  43. reconcile/external_resources/secrets_sync.py +5 -5
  44. reconcile/external_resources/state.py +5 -5
  45. reconcile/gabi_authorized_users.py +3 -6
  46. reconcile/gcr_mirror.py +1 -1
  47. reconcile/github_org.py +1 -3
  48. reconcile/github_repo_invites.py +2 -5
  49. reconcile/gitlab_housekeeping.py +7 -11
  50. reconcile/gitlab_labeler.py +1 -2
  51. reconcile/gitlab_members.py +2 -5
  52. reconcile/gitlab_permissions.py +1 -3
  53. reconcile/glitchtip/integration.py +2 -5
  54. reconcile/glitchtip_project_alerts/integration.py +3 -6
  55. reconcile/glitchtip_project_dsn/integration.py +4 -7
  56. reconcile/integrations_manager.py +5 -8
  57. reconcile/jenkins/types.py +5 -6
  58. reconcile/jenkins_job_builder.py +9 -12
  59. reconcile/jenkins_roles.py +1 -1
  60. reconcile/jira_watcher.py +2 -2
  61. reconcile/ldap_groups/integration.py +2 -5
  62. reconcile/ocm/types.py +21 -26
  63. reconcile/ocm_addons_upgrade_tests_trigger.py +3 -6
  64. reconcile/ocm_clusters.py +8 -8
  65. reconcile/ocm_internal_notifications/integration.py +1 -2
  66. reconcile/ocm_labels/integration.py +2 -5
  67. reconcile/ocm_machine_pools.py +11 -15
  68. reconcile/ocm_upgrade_scheduler_org_updater.py +2 -5
  69. reconcile/openshift_base.py +27 -29
  70. reconcile/openshift_groups.py +15 -20
  71. reconcile/openshift_namespace_labels.py +8 -14
  72. reconcile/openshift_namespaces.py +5 -8
  73. reconcile/openshift_network_policies.py +2 -4
  74. reconcile/openshift_resources_base.py +19 -29
  75. reconcile/openshift_saas_deploy.py +9 -10
  76. reconcile/openshift_saas_deploy_change_tester.py +7 -10
  77. reconcile/openshift_saas_deploy_trigger_base.py +4 -7
  78. reconcile/openshift_saas_deploy_trigger_cleaner.py +5 -8
  79. reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
  80. reconcile/openshift_saas_deploy_trigger_images.py +1 -2
  81. reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
  82. reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
  83. reconcile/openshift_tekton_resources.py +7 -11
  84. reconcile/openshift_upgrade_watcher.py +10 -13
  85. reconcile/openshift_users.py +8 -11
  86. reconcile/oum/base.py +3 -4
  87. reconcile/oum/labelset.py +1 -2
  88. reconcile/oum/metrics.py +2 -2
  89. reconcile/oum/models.py +1 -2
  90. reconcile/oum/standalone.py +2 -3
  91. reconcile/prometheus_rules_tester/integration.py +6 -9
  92. reconcile/quay_membership.py +1 -2
  93. reconcile/quay_mirror.py +12 -13
  94. reconcile/quay_mirror_org.py +10 -10
  95. reconcile/queries.py +4 -7
  96. reconcile/resource_scraper.py +3 -4
  97. reconcile/rhidp/common.py +2 -2
  98. reconcile/saas_auto_promotions_manager/integration.py +5 -6
  99. reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +1 -2
  100. reconcile/saas_auto_promotions_manager/publisher.py +5 -6
  101. reconcile/saas_auto_promotions_manager/subscriber.py +3 -4
  102. reconcile/saas_file_validator.py +2 -5
  103. reconcile/signalfx_endpoint_monitoring.py +2 -5
  104. reconcile/skupper_network/integration.py +3 -6
  105. reconcile/skupper_network/models.py +3 -5
  106. reconcile/slack_base.py +4 -7
  107. reconcile/slack_usergroups.py +15 -17
  108. reconcile/sql_query.py +5 -9
  109. reconcile/status_board.py +4 -5
  110. reconcile/statuspage/atlassian.py +14 -15
  111. reconcile/statuspage/integrations/maintenances.py +3 -3
  112. reconcile/statuspage/page.py +8 -8
  113. reconcile/statuspage/state.py +4 -5
  114. reconcile/statuspage/status.py +7 -8
  115. reconcile/templating/lib/rendering.py +8 -8
  116. reconcile/templating/renderer.py +10 -11
  117. reconcile/templating/validator.py +4 -4
  118. reconcile/terraform_aws_route53.py +3 -6
  119. reconcile/terraform_cloudflare_dns.py +9 -12
  120. reconcile/terraform_cloudflare_resources.py +9 -11
  121. reconcile/terraform_cloudflare_users.py +8 -11
  122. reconcile/terraform_init/integration.py +2 -2
  123. reconcile/terraform_repo.py +11 -14
  124. reconcile/terraform_resources.py +20 -21
  125. reconcile/terraform_tgw_attachments.py +32 -36
  126. reconcile/terraform_users.py +6 -7
  127. reconcile/terraform_vpc_resources/integration.py +5 -5
  128. reconcile/test/conftest.py +7 -10
  129. reconcile/test/fixtures.py +1 -1
  130. reconcile/test/saas_auto_promotions_manager/conftest.py +2 -2
  131. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +2 -2
  132. reconcile/test/test_database_access_manager.py +3 -6
  133. reconcile/test/test_gitlab_labeler.py +2 -5
  134. reconcile/test/test_jump_host.py +5 -8
  135. reconcile/test/test_ocm_machine_pools.py +1 -4
  136. reconcile/test/test_openshift_base.py +3 -6
  137. reconcile/test/test_openshift_cluster_bots.py +5 -5
  138. reconcile/test/test_openshift_namespace_labels.py +2 -3
  139. reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +2 -2
  140. reconcile/test/test_saasherder.py +9 -12
  141. reconcile/test/test_slack_base.py +4 -6
  142. reconcile/test/test_status_board.py +4 -7
  143. reconcile/test/test_terraform_tgw_attachments.py +14 -20
  144. reconcile/typed_queries/alerting_services_settings.py +1 -2
  145. reconcile/typed_queries/app_interface_custom_messages.py +2 -3
  146. reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +1 -3
  147. reconcile/typed_queries/app_interface_repo_url.py +1 -2
  148. reconcile/typed_queries/app_interface_state_settings.py +1 -3
  149. reconcile/typed_queries/app_interface_vault_settings.py +1 -2
  150. reconcile/typed_queries/aws_vpc_requests.py +1 -3
  151. reconcile/typed_queries/aws_vpcs.py +1 -3
  152. reconcile/typed_queries/clusters.py +2 -4
  153. reconcile/typed_queries/clusters_minimal.py +1 -3
  154. reconcile/typed_queries/clusters_with_dms.py +1 -3
  155. reconcile/typed_queries/external_resources.py +3 -4
  156. reconcile/typed_queries/pagerduty_instances.py +1 -2
  157. reconcile/typed_queries/repos.py +2 -3
  158. reconcile/typed_queries/reserved_networks.py +1 -3
  159. reconcile/typed_queries/saas_files.py +49 -59
  160. reconcile/typed_queries/slo_documents.py +1 -3
  161. reconcile/typed_queries/status_board.py +3 -7
  162. reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
  163. reconcile/typed_queries/terraform_namespaces.py +1 -2
  164. reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +1 -3
  165. reconcile/utils/acs/base.py +2 -3
  166. reconcile/utils/acs/notifiers.py +3 -3
  167. reconcile/utils/acs/policies.py +3 -3
  168. reconcile/utils/aggregated_list.py +1 -1
  169. reconcile/utils/amtool.py +1 -2
  170. reconcile/utils/aws_api.py +28 -31
  171. reconcile/utils/binary.py +1 -3
  172. reconcile/utils/clusterhealth/providerbase.py +1 -2
  173. reconcile/utils/clusterhealth/telemeter.py +2 -2
  174. reconcile/utils/deadmanssnitch_api.py +1 -2
  175. reconcile/utils/disabled_integrations.py +4 -6
  176. reconcile/utils/environ.py +1 -1
  177. reconcile/utils/expiration.py +3 -7
  178. reconcile/utils/external_resource_spec.py +3 -4
  179. reconcile/utils/external_resources.py +4 -7
  180. reconcile/utils/filtering.py +1 -2
  181. reconcile/utils/git.py +3 -9
  182. reconcile/utils/git_secrets.py +5 -5
  183. reconcile/utils/github_api.py +5 -9
  184. reconcile/utils/gitlab_api.py +2 -3
  185. reconcile/utils/glitchtip/client.py +2 -4
  186. reconcile/utils/glitchtip/models.py +8 -11
  187. reconcile/utils/gql.py +26 -35
  188. reconcile/utils/grouping.py +1 -3
  189. reconcile/utils/imap_client.py +2 -5
  190. reconcile/utils/internal_groups/client.py +1 -2
  191. reconcile/utils/internal_groups/models.py +8 -9
  192. reconcile/utils/jenkins_api.py +4 -4
  193. reconcile/utils/jinja2/extensions.py +1 -1
  194. reconcile/utils/jinja2/filters.py +4 -4
  195. reconcile/utils/jinja2/utils.py +16 -16
  196. reconcile/utils/jira_client.py +10 -11
  197. reconcile/utils/jjb_client.py +14 -17
  198. reconcile/utils/jobcontroller/controller.py +5 -5
  199. reconcile/utils/jobcontroller/models.py +2 -2
  200. reconcile/utils/jsonpath.py +4 -5
  201. reconcile/utils/jump_host.py +7 -8
  202. reconcile/utils/keycloak.py +3 -7
  203. reconcile/utils/ldap_client.py +2 -3
  204. reconcile/utils/lean_terraform_client.py +13 -17
  205. reconcile/utils/membershipsources/app_interface_resolver.py +1 -1
  206. reconcile/utils/membershipsources/models.py +19 -22
  207. reconcile/utils/metrics.py +13 -15
  208. reconcile/utils/mr/base.py +7 -11
  209. reconcile/utils/mr/glitchtip_access_reporter.py +2 -2
  210. reconcile/utils/mr/notificator.py +1 -2
  211. reconcile/utils/oc.py +32 -38
  212. reconcile/utils/oc_connection_parameters.py +24 -25
  213. reconcile/utils/oc_filters.py +2 -3
  214. reconcile/utils/oc_map.py +9 -15
  215. reconcile/utils/ocm/addons.py +7 -10
  216. reconcile/utils/ocm/base.py +38 -39
  217. reconcile/utils/ocm/clusters.py +6 -9
  218. reconcile/utils/ocm/label_sources.py +1 -2
  219. reconcile/utils/ocm/labels.py +3 -6
  220. reconcile/utils/ocm/ocm.py +11 -14
  221. reconcile/utils/ocm/products.py +1 -3
  222. reconcile/utils/ocm/search_filters.py +16 -17
  223. reconcile/utils/ocm/service_log.py +2 -3
  224. reconcile/utils/ocm/sre_capability_labels.py +4 -8
  225. reconcile/utils/ocm/subscriptions.py +1 -3
  226. reconcile/utils/ocm/syncsets.py +2 -4
  227. reconcile/utils/ocm/upgrades.py +5 -9
  228. reconcile/utils/ocm_base_client.py +13 -16
  229. reconcile/utils/openshift_resource.py +5 -11
  230. reconcile/utils/output.py +2 -3
  231. reconcile/utils/pagerduty_api.py +4 -5
  232. reconcile/utils/prometheus.py +2 -2
  233. reconcile/utils/promotion_state.py +4 -5
  234. reconcile/utils/promtool.py +2 -8
  235. reconcile/utils/quay_api.py +12 -22
  236. reconcile/utils/raw_github_api.py +3 -5
  237. reconcile/utils/rosa/rosa_cli.py +6 -6
  238. reconcile/utils/rosa/session.py +6 -7
  239. reconcile/utils/runtime/desired_state_diff.py +3 -8
  240. reconcile/utils/runtime/environment.py +4 -7
  241. reconcile/utils/runtime/integration.py +4 -4
  242. reconcile/utils/runtime/meta.py +1 -2
  243. reconcile/utils/runtime/runner.py +7 -10
  244. reconcile/utils/runtime/sharding.py +22 -27
  245. reconcile/utils/saasherder/interfaces.py +63 -69
  246. reconcile/utils/saasherder/models.py +30 -35
  247. reconcile/utils/saasherder/saasherder.py +37 -53
  248. reconcile/utils/secret_reader.py +17 -19
  249. reconcile/utils/slack_api.py +15 -17
  250. reconcile/utils/smtp_client.py +1 -2
  251. reconcile/utils/sqs_gateway.py +1 -3
  252. reconcile/utils/state.py +1 -2
  253. reconcile/utils/terraform/config_client.py +4 -5
  254. reconcile/utils/terraform_client.py +3 -8
  255. reconcile/utils/terrascript/cloudflare_client.py +4 -10
  256. reconcile/utils/terrascript/cloudflare_resources.py +10 -13
  257. reconcile/utils/terrascript/models.py +2 -3
  258. reconcile/utils/terrascript/resources.py +1 -2
  259. reconcile/utils/terrascript_aws_client.py +30 -38
  260. reconcile/utils/unleash/client.py +4 -7
  261. reconcile/utils/unleash/server.py +2 -2
  262. reconcile/utils/vault.py +8 -11
  263. reconcile/utils/vaultsecretref.py +2 -3
  264. reconcile/utils/vcs.py +7 -8
  265. reconcile/vault_replication.py +4 -8
  266. reconcile/vpc_peerings_validator.py +4 -9
  267. release/version.py +6 -7
  268. tools/app_interface_reporter.py +2 -2
  269. tools/cli_commands/gpg_encrypt.py +3 -6
  270. tools/cli_commands/systems_and_tools.py +4 -7
  271. tools/qontract_cli.py +31 -17
  272. tools/saas_promotion_state/__init__.py +0 -0
  273. tools/saas_promotion_state/saas_promotion_state.py +72 -0
  274. tools/template_validation.py +1 -1
  275. tools/test/conftest.py +45 -6
  276. tools/test/test_saas_promotion_state.py +86 -0
  277. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/WHEEL +0 -0
  278. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/entry_points.txt +0 -0
  279. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/top_level.txt +0 -0
@@ -4,10 +4,7 @@ from collections.abc import (
4
4
  Iterable,
5
5
  )
6
6
  from functools import lru_cache
7
- from typing import (
8
- Any,
9
- Optional,
10
- )
7
+ from typing import Any
11
8
 
12
9
  from reconcile.utils.ocm.base import (
13
10
  ACTIVE_SUBSCRIPTION_STATES,
@@ -96,7 +93,7 @@ def discover_clusters_by_labels(
96
93
  def discover_clusters_for_subscriptions(
97
94
  ocm_api: OCMBaseClient,
98
95
  subscription_ids: list[str],
99
- cluster_filter: Optional[Filter] = None,
96
+ cluster_filter: Filter | None = None,
100
97
  ) -> list[ClusterDetails]:
101
98
  """
102
99
  Discover clusters by filtering on their subscription IDs.
@@ -118,7 +115,7 @@ def discover_clusters_for_subscriptions(
118
115
  def discover_clusters_for_organizations(
119
116
  ocm_api: OCMBaseClient,
120
117
  organization_ids: Iterable[str],
121
- cluster_filter: Optional[Filter] = None,
118
+ cluster_filter: Filter | None = None,
122
119
  ) -> list[ClusterDetails]:
123
120
  """
124
121
  Discover clusters by filtering on their organization IDs.
@@ -151,8 +148,8 @@ def get_ocm_clusters(
151
148
 
152
149
  def get_cluster_details_for_subscriptions(
153
150
  ocm_api: OCMBaseClient,
154
- subscription_filter: Optional[Filter] = None,
155
- cluster_filter: Optional[Filter] = None,
151
+ subscription_filter: Filter | None = None,
152
+ cluster_filter: Filter | None = None,
156
153
  init_labels: bool = False,
157
154
  ) -> Generator[ClusterDetails, None, None]:
158
155
  """
@@ -227,7 +224,7 @@ def get_node_pools(ocm_api: OCMBaseClient, cluster_id: str) -> list[dict[str, An
227
224
  return results
228
225
 
229
226
 
230
- @lru_cache()
227
+ @lru_cache
231
228
  def get_version(ocm_api: OCMBaseClient, version: str) -> dict[str, Any]:
232
229
  api = f"/api/clusters_mgmt/v1/versions/{version}"
233
230
 
@@ -3,13 +3,12 @@ from abc import (
3
3
  abstractmethod,
4
4
  )
5
5
  from dataclasses import dataclass
6
- from typing import Optional
7
6
 
8
7
 
9
8
  @dataclass(frozen=True)
10
9
  class LabelOwnerRef(ABC):
11
10
  ocm_env: str
12
- label_container_href: Optional[str]
11
+ label_container_href: str | None
13
12
 
14
13
  @abstractmethod
15
14
  def identity_labels(self) -> list[str]:
@@ -1,9 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Generator
3
- from typing import (
4
- Any,
5
- Optional,
6
- )
3
+ from typing import Any
7
4
 
8
5
  from reconcile.utils.ocm.base import (
9
6
  LabelContainer,
@@ -171,7 +168,7 @@ def build_container_for_prefix(
171
168
  )
172
169
 
173
170
 
174
- def label_filter(key: str, value: Optional[str] = None) -> Filter:
171
+ def label_filter(key: str, value: str | None = None) -> Filter:
175
172
  """
176
173
  Creates a filter that matches a label with the given key and
177
174
  optionally a value.
@@ -183,7 +180,7 @@ def label_filter(key: str, value: Optional[str] = None) -> Filter:
183
180
 
184
181
 
185
182
  def get_org_labels(
186
- ocm_api: OCMBaseClient, org_ids: set[str], label_filter: Optional[Filter]
183
+ ocm_api: OCMBaseClient, org_ids: set[str], label_filter: Filter | None
187
184
  ) -> dict[str, LabelContainer]:
188
185
  """
189
186
  Fetch all labels from organizations. Optionally, label filtering can be
@@ -3,10 +3,7 @@ from __future__ import annotations
3
3
  import functools
4
4
  import re
5
5
  from collections.abc import Mapping
6
- from typing import (
7
- Any,
8
- Optional,
9
- )
6
+ from typing import Any
10
7
 
11
8
  from sretoolbox.utils import retry
12
9
 
@@ -96,8 +93,8 @@ class OCM: # pylint: disable=too-many-public-methods
96
93
  init_addons=False,
97
94
  init_version_gates=False,
98
95
  blocked_versions=None,
99
- inheritVersionData: Optional[list[dict[str, Any]]] = None,
100
- product_portfolio: Optional[OCMProductPortfolio] = None,
96
+ inheritVersionData: list[dict[str, Any]] | None = None,
97
+ product_portfolio: OCMProductPortfolio | None = None,
101
98
  ):
102
99
  """Initiates access token and gets clusters information."""
103
100
  self.name = name
@@ -174,7 +171,7 @@ class OCM: # pylint: disable=too-many-public-methods
174
171
  return cluster in self.clusters
175
172
 
176
173
  def get_product_impl(
177
- self, product: str, hypershift: Optional[bool] = False
174
+ self, product: str, hypershift: bool | None = False
178
175
  ) -> OCMProduct:
179
176
  return self.product_portfolio.get_product_impl(product, hypershift)
180
177
 
@@ -255,7 +252,7 @@ class OCM: # pylint: disable=too-many-public-methods
255
252
  )
256
253
  self._delete(api)
257
254
 
258
- def get_cluster_aws_account_id(self, cluster: str) -> Optional[str]:
255
+ def get_cluster_aws_account_id(self, cluster: str) -> str | None:
259
256
  """Returns the AWS account id of the cluster.
260
257
  Since there is no direct API to get this information,
261
258
  we hack our way by relying on existing role grants
@@ -768,7 +765,7 @@ class OCM: # pylint: disable=too-many-public-methods
768
765
  self,
769
766
  cluster: str,
770
767
  with_version: bool = False,
771
- required_state: Optional[str] = None,
768
+ required_state: str | None = None,
772
769
  ) -> list[dict[str, str]]:
773
770
  """Returns a list of Addons installed on a cluster
774
771
 
@@ -784,7 +781,7 @@ class OCM: # pylint: disable=too-many-public-methods
784
781
  return results
785
782
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/addons"
786
783
 
787
- p: Optional[dict[str, Any]] = None
784
+ p: dict[str, Any] | None = None
788
785
  if required_state:
789
786
  p = {"search": f"state='{required_state}'"}
790
787
 
@@ -845,7 +842,7 @@ class OCM: # pylint: disable=too-many-public-methods
845
842
  return rs["kind"].endswith("List")
846
843
 
847
844
  def _get_json(
848
- self, api: str, params: Optional[dict[str, Any]] = None, page_size: int = 100
845
+ self, api: str, params: dict[str, Any] | None = None, page_size: int = 100
849
846
  ) -> dict[str, Any]:
850
847
  responses = []
851
848
  if not params:
@@ -933,7 +930,7 @@ class OCMMap: # pylint: disable=too-many-public-methods
933
930
  init_provision_shards=False,
934
931
  init_addons=False,
935
932
  init_version_gates=False,
936
- product_portfolio: Optional[OCMProductPortfolio] = None,
933
+ product_portfolio: OCMProductPortfolio | None = None,
937
934
  ) -> None:
938
935
  """Initiates OCM instances for each OCM referenced in a cluster."""
939
936
  self.clusters_map: dict[str, str] = {}
@@ -984,7 +981,7 @@ class OCMMap: # pylint: disable=too-many-public-methods
984
981
  init_provision_shards,
985
982
  init_addons,
986
983
  init_version_gates,
987
- product_portfolio: Optional[OCMProductPortfolio] = None,
984
+ product_portfolio: OCMProductPortfolio | None = None,
988
985
  ):
989
986
  if self.cluster_disabled(cluster_info):
990
987
  return
@@ -1009,7 +1006,7 @@ class OCMMap: # pylint: disable=too-many-public-methods
1009
1006
  init_provision_shards,
1010
1007
  init_addons,
1011
1008
  init_version_gates,
1012
- product_portfolio: Optional[OCMProductPortfolio] = None,
1009
+ product_portfolio: OCMProductPortfolio | None = None,
1013
1010
  ):
1014
1011
  """
1015
1012
  Initiate OCM client.
@@ -5,9 +5,7 @@ import random
5
5
  import string
6
6
  from abc import abstractmethod
7
7
  from collections.abc import Mapping
8
- from typing import (
9
- Any,
10
- )
8
+ from typing import Any
11
9
 
12
10
  from pydantic import BaseModel
13
11
 
@@ -5,14 +5,13 @@ from abc import (
5
5
  from collections.abc import Iterable
6
6
  from dataclasses import dataclass
7
7
  from datetime import (
8
+ UTC,
8
9
  datetime,
9
- timezone,
10
10
  )
11
11
  from enum import Enum
12
12
  from typing import (
13
13
  Any,
14
14
  Optional,
15
- Union,
16
15
  )
17
16
 
18
17
  import dateparser
@@ -128,8 +127,8 @@ class DateRangeCondition(FilterCondition):
128
127
  by the dateparser library is supported, e.g. "now", "today", "5 days ago"
129
128
  """
130
129
 
131
- start: Optional[Union[datetime, str]]
132
- end: Optional[Union[datetime, str]]
130
+ start: datetime | str | None
131
+ end: datetime | str | None
133
132
 
134
133
  def copy_and_merge(self, other: "FilterCondition") -> "FilterCondition":
135
134
  raise InvalidFilterError("daterange merge not supported")
@@ -150,7 +149,7 @@ class DateRangeCondition(FilterCondition):
150
149
  conditions.append(f"{self.key} <= '{resolved_end.isoformat()}'")
151
150
  return " and ".join(conditions)
152
151
 
153
- def resolve_start(self) -> Optional[datetime]:
152
+ def resolve_start(self) -> datetime | None:
154
153
  """
155
154
  Resolves the start bound of the date range. If the start bound is a
156
155
  string, it is parsed by the dateparser library. If it is already a
@@ -158,7 +157,7 @@ class DateRangeCondition(FilterCondition):
158
157
  """
159
158
  return DateRangeCondition._resolve_date(self.start) if self.start else None
160
159
 
161
- def resolve_end(self) -> Optional[datetime]:
160
+ def resolve_end(self) -> datetime | None:
162
161
  """
163
162
  Resolves the end bound of the date range. If the end bound is a
164
163
  string, it is parsed by the dateparser library. If it is already a
@@ -166,7 +165,7 @@ class DateRangeCondition(FilterCondition):
166
165
  return DateRangeCondition._resolve_date(self.end) if self.end else None
167
166
 
168
167
  @staticmethod
169
- def _resolve_date(date: Union[datetime, str]) -> datetime:
168
+ def _resolve_date(date: datetime | str) -> datetime:
170
169
  if isinstance(date, datetime):
171
170
  return date
172
171
  parsed = dateparser.parse(
@@ -180,7 +179,7 @@ class DateRangeCondition(FilterCondition):
180
179
 
181
180
  @staticmethod
182
181
  def now() -> datetime:
183
- return datetime.now(tz=timezone.utc)
182
+ return datetime.now(tz=UTC)
184
183
 
185
184
 
186
185
  class InvalidFilterError(Exception):
@@ -210,13 +209,13 @@ class Filter:
210
209
 
211
210
  def __init__(
212
211
  self,
213
- conditions: Optional[list[FilterCondition]] = None,
212
+ conditions: list[FilterCondition] | None = None,
214
213
  mode: FilterMode = FilterMode.AND,
215
214
  ):
216
215
  self.conditions: list[FilterCondition] = conditions or []
217
216
  self.mode = mode
218
217
 
219
- def condition_by_key(self, key: str) -> Optional[FilterCondition]:
218
+ def condition_by_key(self, key: str) -> FilterCondition | None:
220
219
  """
221
220
  Returns the condition with the given key, or None if it does not exist.
222
221
  """
@@ -258,7 +257,7 @@ class Filter:
258
257
  merged_condition = existing_condition.copy_and_merge(condition)
259
258
  return self.copy_and_override(merged_condition)
260
259
 
261
- def eq(self, key: str, value: Optional[str]) -> "Filter":
260
+ def eq(self, key: str, value: str | None) -> "Filter":
262
261
  """
263
262
  Copies the filter and adds a condition to the copy, that requires
264
263
  the given key to be equal to the given value. If the value is None,
@@ -268,7 +267,7 @@ class Filter:
268
267
  return self.is_in(key, [value])
269
268
  return self
270
269
 
271
- def like(self, key: str, value: Optional[str]) -> "Filter":
270
+ def like(self, key: str, value: str | None) -> "Filter":
272
271
  """
273
272
  Copies the filter and adds a condition to the copy, that requires
274
273
  the given key to be similar to the given value based on the % wildcard.
@@ -278,7 +277,7 @@ class Filter:
278
277
  return self.add_condition(LikeCondition(key, [value]))
279
278
  return self
280
279
 
281
- def is_in(self, key: str, values: Optional[Iterable[Any]]) -> "Filter":
280
+ def is_in(self, key: str, values: Iterable[Any] | None) -> "Filter":
282
281
  """
283
282
  Copies the filter and adds a condition to the copy, that requires
284
283
  the given key to be equal to one of the given values. If the values
@@ -293,7 +292,7 @@ class Filter:
293
292
  return self.add_condition(EqCondition(key, value_list))
294
293
  return self
295
294
 
296
- def before(self, key: str, date: Optional[Union[datetime, str]]) -> "Filter":
295
+ def before(self, key: str, date: datetime | str | None) -> "Filter":
297
296
  """
298
297
  Copies the filter and adds a condition to the copy, that requires
299
298
  the given key to be before the given date. If the date is None,
@@ -303,7 +302,7 @@ class Filter:
303
302
  return self.add_condition(DateRangeCondition(key, None, date))
304
303
  return self
305
304
 
306
- def after(self, key: str, date: Optional[Union[datetime, str]]) -> "Filter":
305
+ def after(self, key: str, date: datetime | str | None) -> "Filter":
307
306
  """
308
307
  Copies the filter and adds a condition to the copy, that requires
309
308
  the given key to be after the given date. If the date is None,
@@ -316,8 +315,8 @@ class Filter:
316
315
  def between(
317
316
  self,
318
317
  key: str,
319
- start: Optional[Union[datetime, str]],
320
- end: Optional[Union[datetime, str]],
318
+ start: datetime | str | None,
319
+ end: datetime | str | None,
321
320
  ) -> "Filter":
322
321
  """
323
322
  Copies the filter and adds a condition to the copy, that requires
@@ -3,7 +3,6 @@ from datetime import (
3
3
  datetime,
4
4
  timedelta,
5
5
  )
6
- from typing import Optional
7
6
 
8
7
  from reconcile.utils.ocm.base import (
9
8
  OCMClusterServiceLog,
@@ -17,7 +16,7 @@ CLUSTER_SERVICE_LOGS_CREATE_ENDPOINT = "/api/service_logs/v1/cluster_logs"
17
16
 
18
17
 
19
18
  def get_service_logs_for_cluster_uuid(
20
- ocm_api: OCMBaseClient, cluster_uuid: str, filter: Optional[Filter] = None
19
+ ocm_api: OCMBaseClient, cluster_uuid: str, filter: Filter | None = None
21
20
  ) -> Generator[OCMClusterServiceLog, None, None]:
22
21
  """
23
22
  Returns a list of service logs for a cluster, matching the optional filter.
@@ -36,7 +35,7 @@ def get_service_logs_for_cluster_uuid(
36
35
  def create_service_log(
37
36
  ocm_api: OCMBaseClient,
38
37
  service_log: OCMClusterServiceLogCreateModel,
39
- dedup_interval: Optional[timedelta] = None,
38
+ dedup_interval: timedelta | None = None,
40
39
  ) -> OCMClusterServiceLog:
41
40
  if dedup_interval:
42
41
  previous_log = next(
@@ -1,8 +1,4 @@
1
- from typing import (
2
- Any,
3
- Optional,
4
- Type,
5
- )
1
+ from typing import Any
6
2
 
7
3
  from pydantic import Field
8
4
  from pydantic.fields import ModelField
@@ -15,7 +11,7 @@ from reconcile.utils.ocm.labels import build_container_for_prefix
15
11
 
16
12
 
17
13
  def sre_capability_label_key(
18
- sre_capability: str, config_atom: Optional[str] = None
14
+ sre_capability: str, config_atom: str | None = None
19
15
  ) -> str:
20
16
  """
21
17
  Generates label keys compliant with the naming schema defined in
@@ -34,7 +30,7 @@ def labelset_groupfield(group_prefix: str) -> Any:
34
30
 
35
31
 
36
32
  def build_labelset(
37
- labels: LabelContainer, dataclass: Type[LabelSetTypeVar]
33
+ labels: LabelContainer, dataclass: type[LabelSetTypeVar]
38
34
  ) -> LabelSetTypeVar:
39
35
  """
40
36
  Instantiates a dataclass from a set of labels.
@@ -46,7 +42,7 @@ def build_labelset(
46
42
  return dataclass(**raw_data)
47
43
 
48
44
 
49
- def _labelset_field_value(labels: LabelContainer, field: ModelField) -> Optional[Any]:
45
+ def _labelset_field_value(labels: LabelContainer, field: ModelField) -> Any | None:
50
46
  key_prefix = field.field_info.extra.get("group_by_prefix")
51
47
  if key_prefix:
52
48
  return build_container_for_prefix(
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from pydantic import ValidationError
4
2
 
5
3
  from reconcile.utils.ocm.base import (
@@ -38,7 +36,7 @@ def get_subscriptions(
38
36
 
39
37
 
40
38
  def build_subscription_filter(
41
- states: Optional[set[str]] = None, managed: bool = True
39
+ states: set[str] | None = None, managed: bool = True
42
40
  ) -> Filter:
43
41
  """
44
42
  Helper function to create a subscription search filer for two very common
@@ -1,8 +1,6 @@
1
+ from collections.abc import Mapping
1
2
  from dataclasses import dataclass
2
- from typing import (
3
- Any,
4
- Mapping,
5
- )
3
+ from typing import Any
6
4
 
7
5
  from reconcile.utils.ocm_base_client import OCMBaseClient
8
6
 
@@ -1,8 +1,4 @@
1
- from typing import (
2
- Any,
3
- Optional,
4
- Union,
5
- )
1
+ from typing import Any
6
2
 
7
3
  from reconcile.utils.ocm.base import OCMVersionGate
8
4
  from reconcile.utils.ocm_base_client import OCMBaseClient
@@ -20,7 +16,7 @@ def build_cluster_url(cluster_id: str) -> str:
20
16
 
21
17
 
22
18
  def get_upgrade_policies(
23
- ocm_api: OCMBaseClient, cluster_id: str, schedule_type: Optional[str] = None
19
+ ocm_api: OCMBaseClient, cluster_id: str, schedule_type: str | None = None
24
20
  ) -> list[dict[str, Any]]:
25
21
  """Returns a list of details of Upgrade Policies
26
22
 
@@ -48,7 +44,7 @@ def get_upgrade_policies(
48
44
 
49
45
  def get_upgrade_policy_state(
50
46
  ocm_api: OCMBaseClient, cluster_id: str, upgrade_policy_id: str
51
- ) -> Optional[str]:
47
+ ) -> str | None:
52
48
  try:
53
49
  state_data = ocm_api.get(
54
50
  f"{build_cluster_url(cluster_id)}/upgrade_policies/{upgrade_policy_id}/state"
@@ -80,7 +76,7 @@ def delete_upgrade_policy(
80
76
 
81
77
 
82
78
  def get_control_plane_upgrade_policies(
83
- ocm_api: OCMBaseClient, cluster_id: str, schedule_type: Optional[str] = None
79
+ ocm_api: OCMBaseClient, cluster_id: str, schedule_type: str | None = None
84
80
  ) -> list[dict[str, Any]]:
85
81
  """
86
82
  Returns a list of details of Upgrade Policies
@@ -160,7 +156,7 @@ def create_node_pool_upgrade_policy(
160
156
 
161
157
  def create_version_agreement(
162
158
  ocm_api: OCMBaseClient, gate_id: str, cluster_id: str
163
- ) -> dict[str, Union[str, bool]]:
159
+ ) -> dict[str, str | bool]:
164
160
  return ocm_api.post(
165
161
  f"{build_cluster_url(cluster_id)}/gate_agreements",
166
162
  {"version_gate": {"id": gate_id}},
@@ -6,9 +6,7 @@ from collections.abc import (
6
6
  from types import TracebackType
7
7
  from typing import (
8
8
  Any,
9
- Optional,
10
9
  Protocol,
11
- Type,
12
10
  )
13
11
 
14
12
  from pydantic import BaseModel
@@ -40,7 +38,7 @@ class OCMBaseClient:
40
38
  access_token_client_secret: str,
41
39
  access_token_url: str,
42
40
  access_token_client_id: str,
43
- session: Optional[Session] = None,
41
+ session: Session | None = None,
44
42
  ):
45
43
  self._access_token_client_secret = access_token_client_secret
46
44
  self._access_token_client_id = access_token_client_id
@@ -69,7 +67,7 @@ class OCMBaseClient:
69
67
  "accept": "application/json",
70
68
  })
71
69
 
72
- def get(self, api_path: str, params: Optional[Mapping[str, str]] = None) -> Any:
70
+ def get(self, api_path: str, params: Mapping[str, str] | None = None) -> Any:
73
71
  ocm_request.labels(verb="GET", client_id=self._access_token_client_id).inc()
74
72
  r = self._session.get(
75
73
  f"{self._url}{api_path}",
@@ -82,9 +80,9 @@ class OCMBaseClient:
82
80
  def get_paginated(
83
81
  self,
84
82
  api_path: str,
85
- params: Optional[dict[str, Any]] = None,
83
+ params: dict[str, Any] | None = None,
86
84
  max_page_size: int = 100,
87
- max_pages: Optional[int] = None,
85
+ max_pages: int | None = None,
88
86
  ) -> Generator[dict[str, Any], None, None]:
89
87
  if not params:
90
88
  params_copy = {}
@@ -94,8 +92,7 @@ class OCMBaseClient:
94
92
 
95
93
  while True:
96
94
  rs = self.get(api_path, params=params_copy)
97
- for item in rs.get("items", []):
98
- yield item
95
+ yield from rs.get("items", [])
99
96
  current_page = rs.get("page", 0)
100
97
  records_on_page = rs.get("size", len(rs.get("items", [])))
101
98
  if records_on_page < max_page_size:
@@ -107,8 +104,8 @@ class OCMBaseClient:
107
104
  def post(
108
105
  self,
109
106
  api_path: str,
110
- data: Optional[Mapping[str, Any]] = None,
111
- params: Optional[Mapping[str, str]] = None,
107
+ data: Mapping[str, Any] | None = None,
108
+ params: Mapping[str, str] | None = None,
112
109
  ) -> Any:
113
110
  ocm_request.labels(verb="POST", client_id=self._access_token_client_id).inc()
114
111
  r = self._session.post(
@@ -130,7 +127,7 @@ class OCMBaseClient:
130
127
  self,
131
128
  api_path: str,
132
129
  data: Mapping[str, Any],
133
- params: Optional[Mapping[str, str]] = None,
130
+ params: Mapping[str, str] | None = None,
134
131
  ):
135
132
  ocm_request.labels(verb="PATCH", client_id=self._access_token_client_id).inc()
136
133
  r = self._session.patch(
@@ -158,9 +155,9 @@ class OCMBaseClient:
158
155
 
159
156
  def __exit__(
160
157
  self,
161
- exc_type: Optional[Type[BaseException]],
162
- exc_value: Optional[BaseException],
163
- traceback: Optional[TracebackType],
158
+ exc_type: type[BaseException] | None,
159
+ exc_value: BaseException | None,
160
+ traceback: TracebackType | None,
164
161
  ) -> None:
165
162
  self.close()
166
163
 
@@ -184,7 +181,7 @@ class OCMAPIClientConfiguration(BaseModel, arbitrary_types_allowed=True):
184
181
  def init_ocm_base_client_for_org(
185
182
  org: AUSOCMOrganization,
186
183
  secret_reader: SecretReaderBase,
187
- session: Optional[Session] = None,
184
+ session: Session | None = None,
188
185
  ) -> OCMBaseClient:
189
186
  if org.access_token_client_id:
190
187
  return init_ocm_base_client(
@@ -204,7 +201,7 @@ def init_ocm_base_client_for_org(
204
201
  def init_ocm_base_client(
205
202
  cfg: OCMAPIClientConfigurationProtocol,
206
203
  secret_reader: SecretReaderBase,
207
- session: Optional[Session] = None,
204
+ session: Session | None = None,
208
205
  ) -> OCMBaseClient:
209
206
  """
210
207
  Initiate an API client towards an OCM instance.
@@ -6,10 +6,6 @@ import json
6
6
  import re
7
7
  from collections.abc import Mapping
8
8
  from threading import Lock
9
- from typing import (
10
- Optional,
11
- Union,
12
- )
13
9
 
14
10
  import semver
15
11
  from pydantic import BaseModel
@@ -48,7 +44,7 @@ IGNORABLE_DATA_FIELDS = ["service-ca.crt"]
48
44
  # these labels existance and/or value is determined by a controller running
49
45
  # on the cluster. we need to ignore their existance in the current state,
50
46
  # otherwise we will deal with constant reconciliation
51
- CONTROLLER_MANAGED_LABELS: dict[str, set[Union[str, re.Pattern]]] = {
47
+ CONTROLLER_MANAGED_LABELS: dict[str, set[str | re.Pattern]] = {
52
48
  "ManagedCluster": {
53
49
  "clusterID",
54
50
  "managed-by",
@@ -247,9 +243,7 @@ class OpenshiftResource:
247
243
  self.name # pylint: disable=pointless-statement
248
244
  self.kind # pylint: disable=pointless-statement
249
245
  except (KeyError, TypeError) as e:
250
- msg = "resource invalid data ({}). details: {}".format(
251
- e.__class__.__name__, self.error_details
252
- )
246
+ msg = f"resource invalid data ({e.__class__.__name__}). details: {self.error_details}"
253
247
  raise ConstructResourceError(msg)
254
248
 
255
249
  if self.kind not in {
@@ -581,7 +575,7 @@ class ResourceInventory:
581
575
  cluster,
582
576
  namespace,
583
577
  resource_type,
584
- managed_names: Optional[list[str]] = None,
578
+ managed_names: list[str] | None = None,
585
579
  ):
586
580
  self._clusters.setdefault(cluster, {})
587
581
  self._clusters[cluster].setdefault(namespace, {})
@@ -690,8 +684,8 @@ def build_secret(
690
684
  integration_version: str,
691
685
  unencoded_data: Mapping[str, str],
692
686
  error_details: str = "",
693
- caller_name: Optional[str] = None,
694
- annotations: Optional[Mapping[str, str]] = None,
687
+ caller_name: str | None = None,
688
+ annotations: Mapping[str, str] | None = None,
695
689
  ) -> OpenshiftResource:
696
690
  encoded_data = {
697
691
  k: base64_encode_secret_field_value(v) for k, v in unencoded_data.items()
reconcile/utils/output.py CHANGED
@@ -3,17 +3,16 @@ from collections.abc import (
3
3
  Iterable,
4
4
  Mapping,
5
5
  )
6
- from typing import Optional, Union
7
6
 
8
7
  import yaml
9
8
  from tabulate import tabulate
10
9
 
11
10
 
12
11
  def print_output(
13
- options: Mapping[str, Union[str, bool]],
12
+ options: Mapping[str, str | bool],
14
13
  content: list[dict],
15
14
  columns: Iterable[str] = (),
16
- ) -> Optional[str]:
15
+ ) -> str | None:
17
16
  if options["sort"]:
18
17
  content.sort(key=lambda c: tuple(c.values()))
19
18
  if options.get("to_string"):
@@ -6,7 +6,6 @@ from collections.abc import (
6
6
  from datetime import datetime as dt
7
7
  from datetime import timedelta
8
8
  from typing import (
9
- Optional,
10
9
  Protocol,
11
10
  )
12
11
 
@@ -45,7 +44,7 @@ class PagerDutyUser(Protocol):
45
44
  which must be implemented by a class to be compatible."""
46
45
 
47
46
  org_username: str
48
- pagerduty_username: Optional[str]
47
+ pagerduty_username: str | None
49
48
 
50
49
 
51
50
  class PagerDutyTarget(Protocol):
@@ -54,8 +53,8 @@ class PagerDutyTarget(Protocol):
54
53
 
55
54
  name: str
56
55
  instance: PagerDutyInstance
57
- escalation_policy_id: Optional[str]
58
- schedule_id: Optional[str]
56
+ escalation_policy_id: str | None
57
+ schedule_id: str | None
59
58
 
60
59
 
61
60
  class PagerDutyConfig(BaseModel):
@@ -166,7 +165,7 @@ class PagerDutyMap:
166
165
 
167
166
  def get_pagerduty_map(
168
167
  secret_reader: SecretReader,
169
- pagerduty_instances: Optional[Iterable[PagerDutyInstance]],
168
+ pagerduty_instances: Iterable[PagerDutyInstance] | None,
170
169
  init_users: bool = True,
171
170
  pager_duty_api_class: type[PagerDutyApi] = PagerDutyApi,
172
171
  ) -> PagerDutyMap: