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
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import Optional, Protocol, TextIO
3
+ from typing import Protocol, TextIO
4
4
 
5
5
  from kubernetes.client import ( # type: ignore[attr-defined]
6
6
  ApiClient,
@@ -85,7 +85,7 @@ class K8sJobController:
85
85
  self.oc = oc
86
86
  self.dry_run = dry_run
87
87
  self.time_module = time_module
88
- self._cache: Optional[dict[str, OpenshiftResource]] = None
88
+ self._cache: dict[str, OpenshiftResource] | None = None
89
89
 
90
90
  @property
91
91
  def cache(self) -> dict[str, OpenshiftResource]:
@@ -111,7 +111,7 @@ class K8sJobController:
111
111
  self._cache = new_cache
112
112
  return self._cache
113
113
 
114
- def get_job_generation(self, job_name: str) -> Optional[str]:
114
+ def get_job_generation(self, job_name: str) -> str | None:
115
115
  """
116
116
  Returns the generation annotation for a job.
117
117
  """
@@ -248,7 +248,7 @@ class K8sJobController:
248
248
  return True
249
249
  return False
250
250
 
251
- def _lookup_job_uid(self, job_name: str) -> Optional[str]:
251
+ def _lookup_job_uid(self, job_name: str) -> str | None:
252
252
  job_resource = self.oc.get(
253
253
  self.namespace, "Job", job_name, allow_not_found=True
254
254
  )
@@ -256,7 +256,7 @@ class K8sJobController:
256
256
  return None
257
257
  return job_resource.get("metadata", {}).get("uid")
258
258
 
259
- def build_secret(self, job: K8sJob) -> Optional[V1Secret]:
259
+ def build_secret(self, job: K8sJob) -> V1Secret | None:
260
260
  secret_data = job.secret_data()
261
261
  script_data = job.scripts()
262
262
  # fail if both dicts have overlapping keys
@@ -1,7 +1,7 @@
1
1
  import hashlib
2
2
  import inspect
3
3
  from abc import ABC, abstractmethod
4
- from enum import Enum, IntFlag
4
+ from enum import IntFlag, StrEnum
5
5
  from typing import Any
6
6
 
7
7
  from deepdiff import DeepHash
@@ -19,7 +19,7 @@ from kubernetes.client import (
19
19
  )
20
20
 
21
21
 
22
- class JobStatus(str, Enum):
22
+ class JobStatus(StrEnum):
23
23
  SUCCESS: str = "SUCCESS"
24
24
  ERROR: str = "ERROR"
25
25
  IN_PROGRESS: str = "IN_PROGRESS"
@@ -4,7 +4,6 @@ from functools import (
4
4
  reduce,
5
5
  )
6
6
  from itertools import zip_longest
7
- from typing import Optional
8
7
 
9
8
  import jsonpath_ng
10
9
  import jsonpath_ng.ext.filter
@@ -54,11 +53,11 @@ def narrow_jsonpath_node(
54
53
  if path_2.fields == ("*",):
55
54
  return path_1
56
55
  elif isinstance(path_1, jsonpath_ng.Index) and isinstance(
57
- path_2, (jsonpath_ng.Slice, jsonpath_ng.ext.filter.Filter)
56
+ path_2, jsonpath_ng.Slice | jsonpath_ng.ext.filter.Filter
58
57
  ):
59
58
  return path_1
60
59
  elif isinstance(
61
- path_1, (jsonpath_ng.Slice, jsonpath_ng.ext.filter.Filter)
60
+ path_1, jsonpath_ng.Slice | jsonpath_ng.ext.filter.Filter
62
61
  ) and isinstance(path_2, jsonpath_ng.Index):
63
62
  return path_2
64
63
  elif isinstance(path_1, jsonpath_ng.ext.filter.Filter) and isinstance(
@@ -119,7 +118,7 @@ def apply_constraint_to_path(
119
118
  path: jsonpath_ng.JSONPath,
120
119
  path_constraint: jsonpath_ng.JSONPath,
121
120
  min_common_prefix_length: int = 1,
122
- ) -> Optional[jsonpath_ng.JSONPath]:
121
+ ) -> jsonpath_ng.JSONPath | None:
123
122
  """
124
123
  Narrow the `path` with a more specific `path_constraint`.
125
124
  e.g. if the path constraints a slice `[*]` and the constraints a
@@ -148,7 +147,7 @@ def apply_constraint_to_path(
148
147
 
149
148
  def remove_prefix_from_path(
150
149
  path: jsonpath_ng.JSONPath, prefix: jsonpath_ng.JSONPath
151
- ) -> Optional[jsonpath_ng.JSONPath]:
150
+ ) -> jsonpath_ng.JSONPath | None:
152
151
  path_parts = jsonpath_parts(path, ignore_root=True)
153
152
  prefix_parts = jsonpath_parts(prefix, ignore_root=True)
154
153
 
@@ -4,7 +4,6 @@ import shutil
4
4
  import tempfile
5
5
  import threading
6
6
  from dataclasses import dataclass
7
- from typing import Optional
8
7
 
9
8
  from sshtunnel import SSHTunnelForwarder
10
9
 
@@ -27,9 +26,9 @@ class JumphostParameters:
27
26
  hostname: str
28
27
  known_hosts: str
29
28
  user: str
30
- port: Optional[int]
31
- remote_port: Optional[int]
32
- local_port: Optional[int]
29
+ port: int | None
30
+ remote_port: int | None
31
+ local_port: int | None
33
32
  key: str
34
33
 
35
34
 
@@ -60,7 +59,7 @@ class JumpHostSSH(JumpHostBase):
60
59
  tunnel_lock = threading.Lock()
61
60
 
62
61
  def __init__(
63
- self, parameters: JumphostParameters, gql_api: Optional[gql.GqlApi] = None
62
+ self, parameters: JumphostParameters, gql_api: gql.GqlApi | None = None
64
63
  ):
65
64
  JumpHostBase.__init__(self, parameters=parameters)
66
65
 
@@ -75,7 +74,7 @@ class JumpHostSSH(JumpHostBase):
75
74
  self._remote_port = parameters.remote_port
76
75
 
77
76
  @property
78
- def local_port(self) -> Optional[int]:
77
+ def local_port(self) -> int | None:
79
78
  return self._local_port
80
79
 
81
80
  @staticmethod
@@ -102,7 +101,7 @@ class JumpHostSSH(JumpHostBase):
102
101
  self.known_hosts_file = known_hosts_file
103
102
 
104
103
  def get_ssh_base_cmd(self) -> list[str]:
105
- user_host = "{}@{}".format(self._user, self._hostname)
104
+ user_host = f"{self._user}@{self._hostname}"
106
105
 
107
106
  return [
108
107
  "ssh",
@@ -115,7 +114,7 @@ class JumpHostSSH(JumpHostBase):
115
114
  "-o",
116
115
  "StrictHostKeyChecking=yes",
117
116
  "-o",
118
- "UserKnownHostsFile={}".format(self.known_hosts_file),
117
+ f"UserKnownHostsFile={self.known_hosts_file}",
119
118
  "-i",
120
119
  self._identity_file,
121
120
  "-p",
@@ -1,9 +1,5 @@
1
- from typing import (
2
- Any,
3
- Iterable,
4
- Optional,
5
- Sequence,
6
- )
1
+ from collections.abc import Iterable, Sequence
2
+ from typing import Any
7
3
 
8
4
  import requests
9
5
  from pydantic import BaseModel
@@ -31,7 +27,7 @@ class SSOClient(BaseModel):
31
27
 
32
28
  class KeycloakInstance(BaseModel):
33
29
  url: str
34
- initial_access_token: Optional[str] = None
30
+ initial_access_token: str | None = None
35
31
 
36
32
 
37
33
  class KeycloakAPI:
@@ -1,6 +1,5 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Iterable
3
- from typing import Optional
4
3
 
5
4
  from ldap3 import (
6
5
  ALL,
@@ -30,7 +29,7 @@ class LdapClient:
30
29
  self.connection.unbind()
31
30
 
32
31
  def get_users(self, uids: Iterable[str]) -> set[str]:
33
- user_filter = "".join((f"(uid={u})" for u in uids))
32
+ user_filter = "".join(f"(uid={u})" for u in uids)
34
33
  _, _, results, _ = self.connection.search(
35
34
  self.base_dn, f"(&(objectclass=person)(|{user_filter}))", attributes=["uid"]
36
35
  )
@@ -66,7 +65,7 @@ class LdapClient:
66
65
 
67
66
  @classmethod
68
67
  def from_params(
69
- cls, server_url: str, user: Optional[str], password: Optional[str], base_dn: str
68
+ cls, server_url: str, user: str | None, password: str | None, base_dn: str
70
69
  ) -> "LdapClient":
71
70
  connection = Connection(
72
71
  Server(server_url, get_info=ALL),
@@ -3,11 +3,7 @@ import logging
3
3
  import os
4
4
  import subprocess
5
5
  from collections.abc import Mapping
6
- from typing import (
7
- Any,
8
- Optional,
9
- Tuple,
10
- )
6
+ from typing import Any
11
7
 
12
8
 
13
9
  def state_rm_access_key(working_dirs, account, user):
@@ -15,13 +11,13 @@ def state_rm_access_key(working_dirs, account, user):
15
11
  init_result = subprocess.run(["terraform", "init"], check=False, cwd=wd)
16
12
  if init_result.returncode != 0:
17
13
  return False
18
- resource = "aws_iam_access_key.{}".format(user)
14
+ resource = f"aws_iam_access_key.{user}"
19
15
  result = subprocess.run(["terraform", "state", "rm", resource], check=False, cwd=wd)
20
16
  return result.returncode == 0
21
17
 
22
18
 
23
19
  def _compute_terraform_env(
24
- env: Optional[Mapping[str, str]] = None,
20
+ env: Mapping[str, str] | None = None,
25
21
  ) -> Mapping[str, str]:
26
22
  default_env = os.environ.copy()
27
23
  return default_env if env is None else {**default_env, **env}
@@ -30,8 +26,8 @@ def _compute_terraform_env(
30
26
  def _terraform_command(
31
27
  args: list[str],
32
28
  working_dir: str,
33
- env: Optional[Mapping[str, str]] = None,
34
- ) -> Tuple[int, str, str]:
29
+ env: Mapping[str, str] | None = None,
30
+ ) -> tuple[int, str, str]:
35
31
  result = subprocess.run(
36
32
  args,
37
33
  capture_output=True,
@@ -66,8 +62,8 @@ def show_json(working_dir: str, path: str) -> dict[str, Any]:
66
62
 
67
63
  def init(
68
64
  working_dir: str,
69
- env: Optional[Mapping[str, str]] = None,
70
- ) -> Tuple[int, str, str]:
65
+ env: Mapping[str, str] | None = None,
66
+ ) -> tuple[int, str, str]:
71
67
  """
72
68
  Run terraform init -input=false -no-color.
73
69
 
@@ -84,8 +80,8 @@ def init(
84
80
 
85
81
  def output(
86
82
  working_dir: str,
87
- env: Optional[Mapping[str, str]] = None,
88
- ) -> Tuple[int, str, str]:
83
+ env: Mapping[str, str] | None = None,
84
+ ) -> tuple[int, str, str]:
89
85
  """
90
86
  Run terraform output -json.
91
87
 
@@ -103,8 +99,8 @@ def output(
103
99
  def plan(
104
100
  working_dir: str,
105
101
  out: str,
106
- env: Optional[Mapping[str, str]] = None,
107
- ) -> Tuple[int, str, str]:
102
+ env: Mapping[str, str] | None = None,
103
+ ) -> tuple[int, str, str]:
108
104
  """
109
105
  Run terraform plan -out=<out> -input=false -no-color.
110
106
 
@@ -123,8 +119,8 @@ def plan(
123
119
  def apply(
124
120
  working_dir: str,
125
121
  dir_or_plan: str,
126
- env: Optional[Mapping[str, str]] = None,
127
- ) -> Tuple[int, str, str]:
122
+ env: Mapping[str, str] | None = None,
123
+ ) -> tuple[int, str, str]:
128
124
  """
129
125
  Run terraform apply -input=false -no-color <dir_or_plan>.
130
126
 
@@ -1,6 +1,6 @@
1
1
  import base64
2
+ from collections.abc import Generator
2
3
  from contextlib import contextmanager
3
- from typing import Generator
4
4
 
5
5
  from reconcile import queries
6
6
  from reconcile.gql_definitions.fragments.membership_source import (
@@ -1,11 +1,8 @@
1
- from collections.abc import Sequence
1
+ from collections.abc import Callable, Sequence
2
2
  from typing import (
3
3
  Any,
4
- Callable,
5
- Optional,
6
4
  Protocol,
7
5
  TypeVar,
8
- Union,
9
6
  )
10
7
 
11
8
  from pydantic import (
@@ -34,7 +31,7 @@ class Bot(Protocol):
34
31
  def name(self) -> str: ...
35
32
 
36
33
  @property
37
- def org_username(self) -> Optional[str]: ...
34
+ def org_username(self) -> str | None: ...
38
35
 
39
36
  def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
40
37
 
@@ -50,21 +47,21 @@ class RoleWithMemberships(Protocol):
50
47
  def bots(self) -> Sequence[Bot]: ...
51
48
 
52
49
  @property
53
- def member_sources(self) -> Optional[Sequence[RoleMembershipSource]]: ...
50
+ def member_sources(self) -> Sequence[RoleMembershipSource] | None: ...
54
51
 
55
52
 
56
53
  class RoleUser(BaseModel):
57
54
  name: str
58
55
  org_username: str
59
- github_username: Optional[str]
60
- quay_username: Optional[str]
61
- slack_username: Optional[str]
62
- pagerduty_username: Optional[str]
63
- aws_username: Optional[str]
64
- cloudflare_user: Optional[str]
65
- public_gpg_key: Optional[str]
66
- tag_on_cluster_updates: Optional[bool] = False
67
- tag_on_merge_requests: Optional[bool] = False
56
+ github_username: str | None
57
+ quay_username: str | None
58
+ slack_username: str | None
59
+ pagerduty_username: str | None
60
+ aws_username: str | None
61
+ cloudflare_user: str | None
62
+ public_gpg_key: str | None
63
+ tag_on_cluster_updates: bool | None = False
64
+ tag_on_merge_requests: bool | None = False
68
65
 
69
66
  class Config:
70
67
  extra = Extra.ignore
@@ -72,18 +69,18 @@ class RoleUser(BaseModel):
72
69
 
73
70
  class RoleBot(BaseModel):
74
71
  name: str
75
- description: Optional[str]
76
- org_username: Optional[str]
77
- github_username: Optional[str]
78
- gitlab_username: Optional[str]
79
- openshift_serviceaccount: Optional[str]
80
- quay_username: Optional[str]
72
+ description: str | None
73
+ org_username: str | None
74
+ github_username: str | None
75
+ gitlab_username: str | None
76
+ openshift_serviceaccount: str | None
77
+ quay_username: str | None
81
78
 
82
79
  class Config:
83
80
  extra = Extra.ignore
84
81
 
85
82
 
86
- RoleMember = Union[RoleUser, RoleBot]
83
+ RoleMember = RoleUser | RoleBot
87
84
 
88
85
  ProviderGroup = tuple[str, str]
89
86
 
@@ -12,8 +12,6 @@ from collections.abc import (
12
12
  from types import TracebackType
13
13
  from typing import (
14
14
  Any,
15
- Optional,
16
- Type,
17
15
  TypeVar,
18
16
  )
19
17
 
@@ -190,10 +188,10 @@ class MetricsContainer:
190
188
  def __init__(
191
189
  self,
192
190
  ) -> None:
193
- self._gauges: dict[Type[GaugeMetric], dict[Sequence[str], float]] = defaultdict(
191
+ self._gauges: dict[type[GaugeMetric], dict[Sequence[str], float]] = defaultdict(
194
192
  dict
195
193
  )
196
- self._counters: dict[Type[CounterMetric], dict[Sequence[str], float]] = (
194
+ self._counters: dict[type[CounterMetric], dict[Sequence[str], float]] = (
197
195
  defaultdict(dict)
198
196
  )
199
197
 
@@ -236,7 +234,7 @@ class MetricsContainer:
236
234
 
237
235
  T = TypeVar("T", bound=BaseMetric)
238
236
 
239
- def get_metric_value(self, metric_class: Type[T], **kwargs: Any) -> Optional[float]:
237
+ def get_metric_value(self, metric_class: type[T], **kwargs: Any) -> float | None:
240
238
  """
241
239
  Finds a unique match for the metrics class and labels, and returns its value.
242
240
  If more than one match is found, a ValueError is raised.
@@ -252,7 +250,7 @@ class MetricsContainer:
252
250
  return None
253
251
 
254
252
  def get_metrics(
255
- self, metric_class: Type[T], **kwargs: Any
253
+ self, metric_class: type[T], **kwargs: Any
256
254
  ) -> list[tuple[T, float]]:
257
255
  """
258
256
  Returns all metrics of the given class from this container and all its scopes,
@@ -383,7 +381,7 @@ class _MetricsContext:
383
381
 
384
382
  def __init__(
385
383
  self,
386
- scope: Optional[Hashable],
384
+ scope: Hashable | None,
387
385
  parent: MetricsContainer,
388
386
  aggregate_counters: bool,
389
387
  ):
@@ -408,9 +406,9 @@ class _MetricsContext:
408
406
 
409
407
  def __exit__(
410
408
  self,
411
- exc_type: Optional[Type[BaseException]],
412
- exc_value: Optional[BaseException],
413
- traceback: Optional[TracebackType],
409
+ exc_type: type[BaseException] | None,
410
+ exc_value: BaseException | None,
411
+ traceback: TracebackType | None,
414
412
  ) -> None:
415
413
  if self.scope:
416
414
  self.parent._scopes[self.scope] = self.container
@@ -420,8 +418,8 @@ class _MetricsContext:
420
418
 
421
419
 
422
420
  def transactional_metrics(
423
- scope: Optional[Hashable] = None,
424
- parent_container: Optional[MetricsContainer] = None,
421
+ scope: Hashable | None = None,
422
+ parent_container: MetricsContainer | None = None,
425
423
  aggregate_counters: bool = True,
426
424
  ) -> _MetricsContext:
427
425
  """
@@ -562,9 +560,9 @@ class ErrorRateMetricSet:
562
560
 
563
561
  def __exit__(
564
562
  self: ERMS,
565
- exc_type: Optional[Type[BaseException]],
566
- exc_value: Optional[BaseException],
567
- traceback: Optional[TracebackType],
563
+ exc_type: type[BaseException] | None,
564
+ exc_value: BaseException | None,
565
+ traceback: TracebackType | None,
568
566
  ) -> None:
569
567
  if exc_value:
570
568
  self.fail(exc_value)
@@ -4,11 +4,7 @@ from abc import (
4
4
  ABC,
5
5
  abstractmethod,
6
6
  )
7
- from typing import (
8
- Any,
9
- Optional,
10
- Union,
11
- )
7
+ from typing import Any
12
8
  from uuid import uuid4
13
9
 
14
10
  from gitlab.exceptions import GitlabError
@@ -36,7 +32,7 @@ class MergeRequestProcessingError(Exception):
36
32
  """
37
33
 
38
34
 
39
- MRClient = Union[GitLabApi, SQSGateway]
35
+ MRClient = GitLabApi | SQSGateway
40
36
 
41
37
 
42
38
  class MergeRequestBase(ABC):
@@ -211,7 +207,7 @@ class MergeRequestBase(ABC):
211
207
  from_=gitlab_cli.main_branch, to=self.branch
212
208
  )["diffs"]
213
209
 
214
- def submit(self, cli: MRClient) -> Union[Any, None]:
210
+ def submit(self, cli: MRClient) -> Any | None:
215
211
  if isinstance(cli, GitLabApi):
216
212
  return self.submit_to_gitlab(gitlab_cli=cli)
217
213
 
@@ -226,10 +222,10 @@ def app_interface_email(
226
222
  name: str,
227
223
  subject: str,
228
224
  body: str,
229
- users: Optional[list[str]] = None,
230
- aliases: Optional[list[str]] = None,
231
- aws_accounts: Optional[list[str]] = None,
232
- apps: Optional[list[str]] = None,
225
+ users: list[str] | None = None,
226
+ aliases: list[str] | None = None,
227
+ aws_accounts: list[str] | None = None,
228
+ apps: list[str] | None = None,
233
229
  ) -> str:
234
230
  """Render app-interface-email template."""
235
231
  with open(EMAIL_TEMPLATE, encoding="locale") as file_obj:
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from collections.abc import Sequence
3
- from datetime import date, timezone
3
+ from datetime import UTC, date
4
4
  from datetime import datetime as dt
5
5
  from pathlib import Path
6
6
 
@@ -60,7 +60,7 @@ class UpdateGlitchtipAccessReport(MergeRequestBase):
60
60
  self._glitchtip_access_revalidation_workbook = str(
61
61
  glitchtip_access_revalidation_workbook
62
62
  )
63
- self._isodate = dt.now(tz=timezone.utc).isoformat()
63
+ self._isodate = dt.now(tz=UTC).isoformat()
64
64
  self._dry_run = dry_run
65
65
 
66
66
  @property
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  from datetime import datetime
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  from pydantic import BaseModel
7
6
 
@@ -32,7 +31,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
32
31
  def __init__(
33
32
  self,
34
33
  notification: Notification,
35
- labels: Optional[list[str]] = None,
34
+ labels: list[str] | None = None,
36
35
  email_base_path: Path = Path("data") / "app-interface" / "emails",
37
36
  dry_run: bool = False,
38
37
  ):