qontract-reconcile 0.10.1rc883__py3-none-any.whl → 0.10.1rc885__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 (276) hide show
  1. {qontract_reconcile-0.10.1rc883.dist-info → qontract_reconcile-0.10.1rc885.dist-info}/METADATA +1 -1
  2. {qontract_reconcile-0.10.1rc883.dist-info → qontract_reconcile-0.10.1rc885.dist-info}/RECORD +276 -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 +6 -6
  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 +12 -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 +12 -17
  272. tools/template_validation.py +1 -1
  273. tools/test/conftest.py +3 -6
  274. {qontract_reconcile-0.10.1rc883.dist-info → qontract_reconcile-0.10.1rc885.dist-info}/WHEEL +0 -0
  275. {qontract_reconcile-0.10.1rc883.dist-info → qontract_reconcile-0.10.1rc885.dist-info}/entry_points.txt +0 -0
  276. {qontract_reconcile-0.10.1rc883.dist-info → qontract_reconcile-0.10.1rc885.dist-info}/top_level.txt +0 -0
@@ -5,9 +5,7 @@ from abc import (
5
5
  from collections.abc import Mapping
6
6
  from typing import (
7
7
  Any,
8
- Optional,
9
8
  Protocol,
10
- Union,
11
9
  runtime_checkable,
12
10
  )
13
11
 
@@ -37,20 +35,20 @@ class HasSecret(Protocol):
37
35
 
38
36
  path: str
39
37
  field: str
40
- version: Optional[int]
41
- q_format: Optional[str]
38
+ version: int | None
39
+ q_format: str | None
42
40
 
43
41
 
44
42
  class SecretReaderBase(ABC):
45
43
  @abstractmethod
46
44
  def _read(
47
- self, path: str, field: str, format: Optional[str], version: Optional[int]
45
+ self, path: str, field: str, format: str | None, version: int | None
48
46
  ) -> str:
49
47
  raise NotImplementedError()
50
48
 
51
49
  @abstractmethod
52
50
  def _read_all(
53
- self, path: str, field: str, format: Optional[str], version: Optional[int]
51
+ self, path: str, field: str, format: str | None, version: int | None
54
52
  ) -> dict[str, str]:
55
53
  raise NotImplementedError()
56
54
 
@@ -97,7 +95,7 @@ class SecretReaderBase(ABC):
97
95
  )
98
96
 
99
97
  def read_with_parameters(
100
- self, path: str, field: str, format: Optional[str], version: Optional[int]
98
+ self, path: str, field: str, format: str | None, version: int | None
101
99
  ) -> str:
102
100
  return self._read(
103
101
  path=path,
@@ -107,7 +105,7 @@ class SecretReaderBase(ABC):
107
105
  )
108
106
 
109
107
  def read_all_with_parameters(
110
- self, path: str, field: str, format: Optional[str], version: Optional[int]
108
+ self, path: str, field: str, format: str | None, version: int | None
111
109
  ) -> dict[str, str]:
112
110
  return self._read_all(
113
111
  path=path,
@@ -117,7 +115,7 @@ class SecretReaderBase(ABC):
117
115
  )
118
116
 
119
117
  def _parameters_to_dict(
120
- self, path: str, field: str, format: Optional[str], version: Optional[int]
118
+ self, path: str, field: str, format: str | None, version: int | None
121
119
  ) -> dict[str, Any]:
122
120
  return {
123
121
  "path": path,
@@ -127,7 +125,7 @@ class SecretReaderBase(ABC):
127
125
  }
128
126
 
129
127
  @staticmethod
130
- def to_dict(secret: HasSecret) -> dict[str, Union[str, int, None]]:
128
+ def to_dict(secret: HasSecret) -> dict[str, str | int | None]:
131
129
  """Convenient method to convert Secret class to dictionary."""
132
130
  return {
133
131
  "path": secret.path,
@@ -142,7 +140,7 @@ class VaultSecretReader(SecretReaderBase):
142
140
  Read secrets from vault via a vault_client
143
141
  """
144
142
 
145
- def __init__(self, vault_client: Optional[VaultClient] = None):
143
+ def __init__(self, vault_client: VaultClient | None = None):
146
144
  self._vault_client = vault_client
147
145
 
148
146
  @property
@@ -152,7 +150,7 @@ class VaultSecretReader(SecretReaderBase):
152
150
  return self._vault_client
153
151
 
154
152
  def _read_all(
155
- self, path: str, field: str, format: Optional[str], version: Optional[int]
153
+ self, path: str, field: str, format: str | None, version: int | None
156
154
  ) -> dict[str, str]:
157
155
  try:
158
156
  data = self.vault_client.read_all( # type: ignore[attr-defined] # mypy doesn't recognize the VaultClient.__new__ method
@@ -172,7 +170,7 @@ class VaultSecretReader(SecretReaderBase):
172
170
  return data
173
171
 
174
172
  def _read(
175
- self, path: str, field: str, format: Optional[str], version: Optional[int]
173
+ self, path: str, field: str, format: str | None, version: int | None
176
174
  ) -> str:
177
175
  try:
178
176
  data = self.vault_client.read( # type: ignore[attr-defined] # mypy doesn't recognize the VaultClient.__new__ method
@@ -194,7 +192,7 @@ class ConfigSecretReader(SecretReaderBase):
194
192
  """
195
193
 
196
194
  def _read(
197
- self, path: str, field: str, format: Optional[str], version: Optional[int]
195
+ self, path: str, field: str, format: str | None, version: int | None
198
196
  ) -> str:
199
197
  try:
200
198
  data = config.read(
@@ -210,7 +208,7 @@ class ConfigSecretReader(SecretReaderBase):
210
208
  return data
211
209
 
212
210
  def _read_all(
213
- self, path: str, field: str, format: Optional[str], version: Optional[int]
211
+ self, path: str, field: str, format: str | None, version: int | None
214
212
  ) -> dict[str, str]:
215
213
  try:
216
214
  data = config.read_all(
@@ -242,13 +240,13 @@ class SecretReader(SecretReaderBase):
242
240
  Consider using create_secret_reader() instead.
243
241
  """
244
242
 
245
- def __init__(self, settings: Optional[Mapping] = None) -> None:
243
+ def __init__(self, settings: Mapping | None = None) -> None:
246
244
  """
247
245
  :param settings: app-interface-settings object. It is a dictionary
248
246
  containing `value: true` if Vault is to be used as the secret backend.
249
247
  """
250
248
  self.settings = settings
251
- self._vault_client: Optional[VaultClient] = None
249
+ self._vault_client: VaultClient | None = None
252
250
 
253
251
  @property
254
252
  def vault_client(self) -> VaultClient:
@@ -257,7 +255,7 @@ class SecretReader(SecretReaderBase):
257
255
  return self._vault_client
258
256
 
259
257
  def _read(
260
- self, path: str, field: str, format: Optional[str], version: Optional[int]
258
+ self, path: str, field: str, format: str | None, version: int | None
261
259
  ) -> str:
262
260
  """Returns a value of a key from Vault secret or configuration file.
263
261
  The input secret is a dictionary which contains the following fields:
@@ -292,7 +290,7 @@ class SecretReader(SecretReaderBase):
292
290
  return data
293
291
 
294
292
  def _read_all(
295
- self, path: str, field: str, format: Optional[str], version: Optional[int]
293
+ self, path: str, field: str, format: str | None, version: int | None
296
294
  ) -> dict[str, str]:
297
295
  """Returns a dictionary of keys and values
298
296
  from Vault secret or configuration file.
@@ -9,9 +9,7 @@ from collections.abc import (
9
9
  )
10
10
  from typing import (
11
11
  Any,
12
- Optional,
13
12
  Protocol,
14
- Union,
15
13
  )
16
14
 
17
15
  from slack_sdk import WebClient
@@ -46,17 +44,17 @@ class ServerErrorRetryHandler(RetryHandler):
46
44
  *,
47
45
  state: RetryState,
48
46
  request: HttpRequest,
49
- response: Optional[HttpResponse] = None,
50
- error: Optional[Exception] = None,
47
+ response: HttpResponse | None = None,
48
+ error: Exception | None = None,
51
49
  ) -> bool:
52
50
  return response is not None and response.status_code >= 500
53
51
 
54
52
 
55
53
  class HasClientGlobalConfig(Protocol):
56
- max_retries: Optional[int]
57
- timeout: Optional[int]
54
+ max_retries: int | None
55
+ timeout: int | None
58
56
 
59
- def dict(self) -> dict[str, Optional[int]]: ...
57
+ def dict(self) -> dict[str, int | None]: ...
60
58
 
61
59
 
62
60
  class HasClientMethodConfig(Protocol):
@@ -68,10 +66,10 @@ class HasClientMethodConfig(Protocol):
68
66
 
69
67
  class HasClientConfig(Protocol):
70
68
  @property
71
- def q_global(self) -> Optional[HasClientGlobalConfig]: ...
69
+ def q_global(self) -> HasClientGlobalConfig | None: ...
72
70
 
73
71
  @property
74
- def methods(self) -> Optional[Sequence[HasClientMethodConfig]]: ...
72
+ def methods(self) -> Sequence[HasClientMethodConfig] | None: ...
75
73
 
76
74
 
77
75
  class SlackApiConfig:
@@ -95,7 +93,7 @@ class SlackApiConfig:
95
93
  """
96
94
  self._methods[method_name] = method_config
97
95
 
98
- def get_method_config(self, method_name: str) -> Optional[dict[str, Any]]:
96
+ def get_method_config(self, method_name: str) -> dict[str, Any] | None:
99
97
  """
100
98
  Get Slack method configuration.
101
99
  :param method_name: the name of a method (ex. users.list)
@@ -165,10 +163,10 @@ class SlackApi:
165
163
  self,
166
164
  workspace_name: str,
167
165
  token: str,
168
- api_config: Optional[SlackApiConfig] = None,
166
+ api_config: SlackApiConfig | None = None,
169
167
  init_usergroups: bool = True,
170
- channel: Optional[str] = None,
171
- slack_url: Optional[str] = None,
168
+ channel: str | None = None,
169
+ slack_url: str | None = None,
172
170
  **chat_kwargs: Any,
173
171
  ) -> None:
174
172
  """
@@ -295,7 +293,7 @@ class SlackApi:
295
293
  if not info.data["channel"]["is_member"]: # type: ignore[call-overload]
296
294
  self._sc.conversations_join(channel=channel_id)
297
295
 
298
- def get_usergroup_id(self, handle: str) -> Optional[str]:
296
+ def get_usergroup_id(self, handle: str) -> str | None:
299
297
  try:
300
298
  return self.get_usergroup(handle)["id"]
301
299
  except UsergroupNotFoundException:
@@ -449,7 +447,7 @@ class SlackApi:
449
447
  result_key = "members" if resource == "users" else resource
450
448
  api_key = "conversations" if resource == "channels" else resource
451
449
  results = {}
452
- additional_kwargs: dict[str, Union[str, int]] = {"cursor": ""}
450
+ additional_kwargs: dict[str, str | int] = {"cursor": ""}
453
451
 
454
452
  method_config = self.config.get_method_config(f"{api_key}.list")
455
453
  if method_config:
@@ -459,7 +457,7 @@ class SlackApi:
459
457
  slack_request.labels(f"{api_key}.list", "GET").inc()
460
458
 
461
459
  result = self._sc.api_call(
462
- "{}.list".format(api_key), http_verb="GET", params=additional_kwargs
460
+ f"{api_key}.list", http_verb="GET", params=additional_kwargs
463
461
  )
464
462
 
465
463
  for r in result[result_key]:
@@ -487,7 +485,7 @@ class SlackApi:
487
485
  return self._enterprise_user_id_to_user_ids.get(user_id, user_id)
488
486
 
489
487
  def get_flat_conversation_history(
490
- self, from_timestamp: int, to_timestamp: Optional[int]
488
+ self, from_timestamp: int, to_timestamp: int | None
491
489
  ) -> list[dict[str, Any]]:
492
490
  """Calls conversation_history method to get all messages in a channel between
493
491
  from_timestamp to to_timestamp ignoring threads"""
@@ -4,7 +4,6 @@ from email.header import Header
4
4
  from email.mime.multipart import MIMEMultipart
5
5
  from email.mime.text import MIMEText
6
6
  from email.utils import formataddr
7
- from typing import Optional
8
7
 
9
8
  from pydantic import (
10
9
  BaseModel,
@@ -54,7 +53,7 @@ class SmtpClient:
54
53
  self.passwd = server.password
55
54
  self.mail_address = mail_address
56
55
  self.timeout = timeout
57
- self._client: Optional[smtplib.SMTP] = None
56
+ self._client: smtplib.SMTP | None = None
58
57
 
59
58
  @property
60
59
  def client(self) -> smtplib.SMTP:
@@ -46,9 +46,7 @@ class SQSGateway:
46
46
  a["name"] for a in accounts if a["uid"] == queue_account_uid
47
47
  ]
48
48
  if len(queue_account_name) != 1:
49
- raise SQSGatewayInitError(
50
- "account uid not found: {}".format(queue_account_uid)
51
- )
49
+ raise SQSGatewayInitError(f"account uid not found: {queue_account_uid}")
52
50
  return queue_account_name[0]
53
51
 
54
52
  def send_message(self, body):
reconcile/utils/state.py CHANGED
@@ -8,7 +8,6 @@ from dataclasses import dataclass, field
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
- Optional,
12
11
  Self,
13
12
  )
14
13
 
@@ -46,7 +45,7 @@ class StateInaccessibleException(Exception):
46
45
 
47
46
  def init_state(
48
47
  integration: str,
49
- secret_reader: Optional[SecretReaderBase] = None,
48
+ secret_reader: SecretReaderBase | None = None,
50
49
  ) -> "State":
51
50
  if not secret_reader:
52
51
  vault_settings = get_app_interface_vault_settings()
@@ -4,7 +4,6 @@ from abc import (
4
4
  abstractmethod,
5
5
  )
6
6
  from collections.abc import Iterable
7
- from typing import Optional
8
7
 
9
8
  from reconcile.utils.exceptions import PrintToFileInGitRepositoryError
10
9
  from reconcile.utils.external_resource_spec import (
@@ -34,7 +33,7 @@ class TerraformConfigClient(ABC):
34
33
  @abstractmethod
35
34
  def dump(
36
35
  self,
37
- existing_dir: Optional[str] = None,
36
+ existing_dir: str | None = None,
38
37
  ) -> str:
39
38
  """Dump the Terraform JSON configuration to the filesystem."""
40
39
 
@@ -65,7 +64,7 @@ class TerraformConfigClientCollection:
65
64
  def add_specs(
66
65
  self,
67
66
  specs: Iterable[ExternalResourceSpec],
68
- account_filter: Optional[str] = None,
67
+ account_filter: str | None = None,
69
68
  ) -> None:
70
69
  """
71
70
  Add external resource specs
@@ -94,8 +93,8 @@ class TerraformConfigClientCollection:
94
93
 
95
94
  def dump(
96
95
  self,
97
- print_to_file: Optional[str] = None,
98
- existing_dirs: Optional[dict[str, str]] = None,
96
+ print_to_file: str | None = None,
97
+ existing_dirs: dict[str, str] | None = None,
99
98
  ) -> dict[str, str]:
100
99
  """
101
100
  Dump the Terraform JSON config to the filesystem.
@@ -20,7 +20,6 @@ from threading import Lock
20
20
  from typing import (
21
21
  IO,
22
22
  Any,
23
- Optional,
24
23
  cast,
25
24
  )
26
25
 
@@ -81,7 +80,7 @@ class TerraformClient: # pylint: disable=too-many-public-methods
81
80
  accounts: Iterable[Mapping[str, Any]],
82
81
  working_dirs: Mapping[str, str],
83
82
  thread_pool_size: int,
84
- aws_api: Optional[AWSApi] = None,
83
+ aws_api: AWSApi | None = None,
85
84
  init_users=False,
86
85
  ):
87
86
  self.integration = integration
@@ -209,6 +208,15 @@ class TerraformClient: # pylint: disable=too-many-public-methods
209
208
  self.created_users.extend(created_users)
210
209
  return disabled_deletions_detected, errors
211
210
 
211
+ def safe_plan(self, enable_deletion: bool) -> None:
212
+ """Raises exception if errors are detected at plan step"""
213
+ disable_deletions_detected, errors = self.plan(enable_deletion)
214
+
215
+ if errors:
216
+ raise RuntimeError("Terraform plan has errors")
217
+ if disable_deletions_detected:
218
+ raise RuntimeError("Terraform plan has disabled deletions detected")
219
+
212
220
  @retry()
213
221
  def terraform_plan(
214
222
  self, spec: TerraformSpec, enable_deletion: bool
@@ -463,12 +471,8 @@ class TerraformClient: # pylint: disable=too-many-public-methods
463
471
  if output is None:
464
472
  return data
465
473
 
466
- enc_pass_pfx = "{}_{}".format(
467
- self.integration_prefix, self.OUTPUT_TYPE_PASSWORDS
468
- )
469
- console_urls_pfx = "{}_{}".format(
470
- self.integration_prefix, self.OUTPUT_TYPE_CONSOLEURLS
471
- )
474
+ enc_pass_pfx = f"{self.integration_prefix}_{self.OUTPUT_TYPE_PASSWORDS}"
475
+ console_urls_pfx = f"{self.integration_prefix}_{self.OUTPUT_TYPE_CONSOLEURLS}"
472
476
  for k, v in output.items():
473
477
  # the integration creates outputs of the form
474
478
  # 0.11: output_secret_name[secret_key] = secret_value
@@ -5,10 +5,6 @@ from abc import (
5
5
  )
6
6
  from collections.abc import Iterable
7
7
  from dataclasses import dataclass
8
- from typing import (
9
- Optional,
10
- Union,
11
- )
12
8
 
13
9
  from terrascript import (
14
10
  Backend,
@@ -175,7 +171,7 @@ class TerrascriptCloudflareClient(TerraformConfigClient):
175
171
  resources_to_add = create_cloudflare_terrascript_resource(spec)
176
172
  self._add_resources(resources_to_add)
177
173
 
178
- def dump(self, existing_dir: Optional[str] = None) -> str:
174
+ def dump(self, existing_dir: str | None = None) -> str:
179
175
  """Write the Terraform JSON representation of the resources to disk"""
180
176
  if existing_dir is None:
181
177
  working_dir = tempfile.mkdtemp(prefix=TMP_DIR_PREFIX)
@@ -192,9 +188,7 @@ class TerrascriptCloudflareClient(TerraformConfigClient):
192
188
  """Return the Terraform JSON representation of the resources"""
193
189
  return str(self._terrascript)
194
190
 
195
- def _add_resources(
196
- self, tf_resources: Iterable[Union[Resource, Output, Data]]
197
- ) -> None:
191
+ def _add_resources(self, tf_resources: Iterable[Resource | Output | Data]) -> None:
198
192
  for resource in tf_resources:
199
193
  self._terrascript.add(resource)
200
194
 
@@ -277,7 +271,7 @@ class TerrascriptCloudflareClientFactory:
277
271
  cls,
278
272
  tf_state_s3: TerraformStateS3,
279
273
  cf_acct: CloudflareAccount,
280
- sharding_strategy: Optional[TerraformS3StateNamingStrategy],
274
+ sharding_strategy: TerraformS3StateNamingStrategy | None,
281
275
  secret_reader: SecretReaderBase,
282
276
  is_managed_account: bool,
283
277
  provider_rps: int = DEFAULT_PROVIDER_RPS,
@@ -300,7 +294,7 @@ class TerrascriptCloudflareClientFactory:
300
294
 
301
295
  def _get_terraform_s3_state_key_name(
302
296
  integration: Integration,
303
- sharding_strategy: Optional[TerraformS3StateNamingStrategy],
297
+ sharding_strategy: TerraformS3StateNamingStrategy | None,
304
298
  ) -> str:
305
299
  if sharding_strategy is None:
306
300
  sharding_strategy = Default()
@@ -2,10 +2,7 @@ from collections.abc import (
2
2
  Iterable,
3
3
  MutableMapping,
4
4
  )
5
- from typing import (
6
- Any,
7
- Union,
8
- )
5
+ from typing import Any
9
6
 
10
7
  from terrascript import (
11
8
  Data,
@@ -90,7 +87,7 @@ class cloudflare_account_roles(Data):
90
87
  # TODO: rename to include object?
91
88
  def create_cloudflare_terrascript_resource(
92
89
  spec: ExternalResourceSpec,
93
- ) -> list[Union[Resource, Output, Data]]:
90
+ ) -> list[Resource | Output | Data]:
94
91
  """
95
92
  Create the required Cloudflare Terrascript resources as defined by the external
96
93
  resources spec.
@@ -117,7 +114,7 @@ def create_cloudflare_terrascript_resource(
117
114
  class CloudflareWorkerScriptTerrascriptResource(TerrascriptResource):
118
115
  """Generate a cloudflare_worker_script resource"""
119
116
 
120
- def populate(self) -> list[Union[Resource, Output]]:
117
+ def populate(self) -> list[Resource | Output]:
121
118
  values = ResourceValueResolver(self._spec).resolve()
122
119
 
123
120
  gh_repo = values["content_from_github"]["repo"]
@@ -164,7 +161,7 @@ class CloudflareZoneTerrascriptResource(TerrascriptResource):
164
161
 
165
162
  def _create_cloudflare_certificate_pack(
166
163
  self, zone: Resource, zone_certs: Iterable[MutableMapping[str, Any]]
167
- ) -> list[Union[Resource, Output]]:
164
+ ) -> list[Resource | Output]:
168
165
  resources = []
169
166
  for cert_values in zone_certs:
170
167
  identifier = safe_resource_id(cert_values.pop("identifier"))
@@ -192,7 +189,7 @@ class CloudflareZoneTerrascriptResource(TerrascriptResource):
192
189
  self,
193
190
  zone: Resource,
194
191
  zone_custom_ssl: Iterable[MutableMapping[str, Any]],
195
- ) -> list[Union[Resource, Output]]:
192
+ ) -> list[Resource | Output]:
196
193
  vault_settings = get_app_interface_vault_settings()
197
194
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
198
195
 
@@ -221,7 +218,7 @@ class CloudflareZoneTerrascriptResource(TerrascriptResource):
221
218
  resources.append(custom_ssl)
222
219
  return resources
223
220
 
224
- def populate(self) -> list[Union[Resource, Output]]:
221
+ def populate(self) -> list[Resource | Output]:
225
222
  resources = []
226
223
 
227
224
  values = ResourceValueResolver(self._spec).resolve()
@@ -313,7 +310,7 @@ class CloudflareZoneTerrascriptResource(TerrascriptResource):
313
310
 
314
311
 
315
312
  class CloudflareAccountMemberTerrascriptResource(TerrascriptResource):
316
- def populate(self) -> list[Union[Resource, Output]]:
313
+ def populate(self) -> list[Resource | Output]:
317
314
  resources = []
318
315
  values = ResourceValueResolver(self._spec).resolve()
319
316
  data_source_cloudflare_account_roles = values.pop("cloudflare_account_roles")
@@ -340,7 +337,7 @@ class CloudflareLogpushJob(TerrascriptResource):
340
337
  we are defining this as inner class.
341
338
  """
342
339
 
343
- def populate(self) -> list[Union[Resource, Output, Data]]:
340
+ def populate(self) -> list[Resource | Output | Data]:
344
341
  resources = []
345
342
  values = ResourceValueResolver(self._spec).resolve()
346
343
  zone = values.pop("zone_name", None)
@@ -373,7 +370,7 @@ class CloudflareLogpushOwnershipChallengeResource(TerrascriptResource):
373
370
  we are defining this as inner class.
374
371
  """
375
372
 
376
- def populate(self) -> list[Union[Resource, Output, Data]]:
373
+ def populate(self) -> list[Resource | Output | Data]:
377
374
  resources = []
378
375
  values = ResourceValueResolver(self._spec).resolve()
379
376
  destination_conf = values.get("destination_conf")
@@ -412,7 +409,7 @@ class CloudflareLogpullRetention(TerrascriptResource):
412
409
  we are defining this as inner class.
413
410
  """
414
411
 
415
- def populate(self) -> list[Union[Resource, Output, Data]]:
412
+ def populate(self) -> list[Resource | Output | Data]:
416
413
  resources = []
417
414
  values = ResourceValueResolver(self._spec).resolve()
418
415
 
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
2
 
4
3
  from reconcile.utils.secret_reader import HasSecret
5
4
 
@@ -22,6 +21,6 @@ class TerraformStateS3:
22
21
  class CloudflareAccount:
23
22
  name: str
24
23
  api_credentials: HasSecret
25
- enforce_twofactor: Optional[bool]
26
- type: Optional[str]
24
+ enforce_twofactor: bool | None
25
+ type: str | None
27
26
  provider_version: str
@@ -3,7 +3,6 @@ from abc import (
3
3
  abstractmethod,
4
4
  )
5
5
  from collections.abc import Iterable
6
- from typing import Union
7
6
 
8
7
  from terrascript import (
9
8
  Data,
@@ -40,5 +39,5 @@ class TerrascriptResource(ABC):
40
39
  ]
41
40
 
42
41
  @abstractmethod
43
- def populate(self) -> list[Union[Resource, Output, Data]]:
42
+ def populate(self) -> list[Resource | Output | Data]:
44
43
  """Calling this method should return the Terrascript resources to be created."""