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,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  from pydantic import BaseModel
4
4
 
@@ -11,7 +11,7 @@ class Scope(BaseModel):
11
11
  """
12
12
 
13
13
  cluster: str
14
- namespace: Optional[str]
14
+ namespace: str | None
15
15
 
16
16
 
17
17
  class PolicyCondition(BaseModel):
@@ -23,7 +23,7 @@ class PolicyCondition(BaseModel):
23
23
  """
24
24
 
25
25
  field_name: str
26
- negate: Optional[bool]
26
+ negate: bool | None
27
27
  values: list[str]
28
28
 
29
29
 
@@ -93,7 +93,7 @@ class AggregatedDiffRunner:
93
93
 
94
94
  def register(self, on, action, cond=None):
95
95
  if on not in self.diff.keys():
96
- raise Exception("Unknown diff key for 'on': {}".format(on))
96
+ raise Exception(f"Unknown diff key for 'on': {on}")
97
97
  self.actions.append((on, action, cond))
98
98
 
99
99
  def run(self):
reconcile/utils/amtool.py CHANGED
@@ -2,7 +2,6 @@ import re
2
2
  import tempfile
3
3
  from collections.abc import Mapping
4
4
  from subprocess import (
5
- PIPE,
6
5
  CalledProcessError,
7
6
  run,
8
7
  )
@@ -62,7 +61,7 @@ def version() -> AmtoolResult:
62
61
 
63
62
  def _run_cmd(cmd: list[str]) -> AmtoolResult:
64
63
  try:
65
- result = run(cmd, stdout=PIPE, stderr=PIPE, check=True)
64
+ result = run(cmd, capture_output=True, check=True)
66
65
  except CalledProcessError as e:
67
66
  msg = f'Error running amtool command [{" ".join(cmd)}]'
68
67
  if e.stdout:
@@ -15,8 +15,6 @@ from typing import (
15
15
  TYPE_CHECKING,
16
16
  Any,
17
17
  Literal,
18
- Optional,
19
- Union,
20
18
  )
21
19
 
22
20
  import botocore
@@ -83,7 +81,7 @@ class MissingARNError(Exception):
83
81
  pass
84
82
 
85
83
 
86
- KeyStatus = Union[Literal["Active"], Literal["Inactive"]]
84
+ KeyStatus = Literal["Active"] | Literal["Inactive"]
87
85
 
88
86
  GOVCLOUD_PARTITION = "aws-us-gov"
89
87
 
@@ -191,7 +189,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
191
189
  self,
192
190
  session: Session,
193
191
  service_name,
194
- region_name: Optional[str] = None,
192
+ region_name: str | None = None,
195
193
  ):
196
194
  region = region_name if region_name else session.region_name
197
195
  client = session.client(
@@ -205,49 +203,49 @@ class AWSApi: # pylint: disable=too-many-public-methods
205
203
  @staticmethod
206
204
  # pylint: disable=method-hidden
207
205
  def _get_session_resource(
208
- session: Session, service_name, region_name: Optional[str] = None
206
+ session: Session, service_name, region_name: str | None = None
209
207
  ):
210
208
  region = region_name if region_name else session.region_name
211
209
  return session.resource(service_name, region_name=region)
212
210
 
213
211
  def _account_ec2_client(
214
- self, account_name: str, region_name: Optional[str] = None
212
+ self, account_name: str, region_name: str | None = None
215
213
  ) -> EC2Client:
216
214
  session = self.get_session(account_name)
217
215
  return self.get_session_client(session, "ec2", region_name)
218
216
 
219
217
  def _account_ec2_resource(
220
- self, account_name: str, region_name: Optional[str] = None
218
+ self, account_name: str, region_name: str | None = None
221
219
  ) -> EC2ServiceResource:
222
220
  session = self.get_session(account_name)
223
221
  return self._get_session_resource(session, "ec2", region_name)
224
222
 
225
223
  def _account_route53_client(
226
- self, account_name: str, region_name: Optional[str] = None
224
+ self, account_name: str, region_name: str | None = None
227
225
  ) -> Route53Client:
228
226
  session = self.get_session(account_name)
229
227
  return self.get_session_client(session, "route53", region_name)
230
228
 
231
229
  def _account_rds_client(
232
- self, account_name: str, region_name: Optional[str] = None
230
+ self, account_name: str, region_name: str | None = None
233
231
  ) -> RDSClient:
234
232
  session = self.get_session(account_name)
235
233
  return self.get_session_client(session, "rds", region_name)
236
234
 
237
235
  def _account_cloudwatch_client(
238
- self, account_name: str, region_name: Optional[str] = None
236
+ self, account_name: str, region_name: str | None = None
239
237
  ):
240
238
  session = self.get_session(account_name)
241
239
  return self.get_session_client(session, "logs", region_name)
242
240
 
243
241
  def _account_organizations_client(
244
- self, account_name: str, region_name: Optional[str] = None
242
+ self, account_name: str, region_name: str | None = None
245
243
  ) -> OrganizationsClient:
246
244
  session = self.get_session(account_name)
247
245
  return self.get_session_client(session, "organizations", region_name)
248
246
 
249
247
  def _account_s3_client(
250
- self, account_name: str, region_name: Optional[str] = None
248
+ self, account_name: str, region_name: str | None = None
251
249
  ) -> S3Client:
252
250
  session = self.get_session(account_name)
253
251
  return self.get_session_client(session, "s3", region_name)
@@ -493,7 +491,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
493
491
 
494
492
  @staticmethod
495
493
  def resource_has_special_name(account, type, resource):
496
- skip_msg = "[{}] skipping {} ".format(account, type) + "({} related) {}"
494
+ skip_msg = f"[{account}] skipping {type} " + "({} related) {}"
497
495
 
498
496
  ignore_names = {
499
497
  "production": ["prod"],
@@ -510,7 +508,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
510
508
  return False
511
509
 
512
510
  def resource_has_special_tags(self, account, type, resource, tags):
513
- skip_msg = "[{}] skipping {} ".format(account, type) + "({}={}) {}"
511
+ skip_msg = f"[{account}] skipping {type} " + "({}={}) {}"
514
512
 
515
513
  ignore_tags = {
516
514
  "ENV": ["prod", "stage", "staging"],
@@ -616,8 +614,8 @@ class AWSApi: # pylint: disable=too-many-public-methods
616
614
  if managed_by_integration_tag[0] == "terraform_resources":
617
615
  return "service_account"
618
616
 
619
- huh = "unrecognized managed_by_integration tag: {}".format(
620
- managed_by_integration_tag[0]
617
+ huh = (
618
+ f"unrecognized managed_by_integration tag: {managed_by_integration_tag[0]}"
621
619
  )
622
620
  raise InvalidResourceTypeError(huh)
623
621
 
@@ -813,7 +811,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
813
811
  @staticmethod
814
812
  def _get_account_assume_data(
815
813
  account: awsh.Account,
816
- ) -> tuple[str, Optional[str], str]:
814
+ ) -> tuple[str, str | None, str]:
817
815
  """
818
816
  returns mandatory data to be able to assume a role with this account:
819
817
  (account_name, assume_role, assume_region)
@@ -823,7 +821,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
823
821
  ok = all(elem in account.keys() for elem in required_keys)
824
822
  if not ok:
825
823
  account_name = account.get("name")
826
- raise KeyError("[{}] account is missing required keys".format(account_name))
824
+ raise KeyError(f"[{account_name}] account is missing required keys")
827
825
  return (account["name"], account.get("assume_role"), account["assume_region"])
828
826
 
829
827
  @staticmethod
@@ -863,7 +861,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
863
861
  def _get_assumed_role_client(
864
862
  self,
865
863
  account_name: str,
866
- assume_role: Optional[str],
864
+ assume_role: str | None,
867
865
  assume_region: str,
868
866
  client_type="ec2",
869
867
  ) -> EC2Client:
@@ -893,7 +891,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
893
891
  # filters a list of aws resources according to tags
894
892
  @staticmethod
895
893
  def filter_on_tags(
896
- items: Iterable[Any], tags: Optional[Mapping[str, str]] = None
894
+ items: Iterable[Any], tags: Mapping[str, str] | None = None
897
895
  ) -> list[Any]:
898
896
  if tags is None:
899
897
  tags = {}
@@ -1054,7 +1052,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1054
1052
  account: Mapping[str, Any],
1055
1053
  owner_account: Mapping[str, Any],
1056
1054
  regex: str,
1057
- region: Optional[str] = None,
1055
+ region: str | None = None,
1058
1056
  ) -> list[dict[str, Any]]:
1059
1057
  ec2 = self._account_ec2_client(account["name"], region_name=region)
1060
1058
  images = self.get_account_amis(ec2, owner=owner_account["uid"])
@@ -1065,7 +1063,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1065
1063
  account: Mapping[str, Any],
1066
1064
  share_account_uid: str,
1067
1065
  image_id: str,
1068
- region: Optional[str] = None,
1066
+ region: str | None = None,
1069
1067
  ):
1070
1068
  ec2 = self._account_ec2_resource(account["name"], region)
1071
1069
  image = ec2.Image(image_id)
@@ -1100,8 +1098,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1100
1098
  client = self._account_cloudwatch_client(account_name, region_name=region_name)
1101
1099
  paginator = client.get_paginator("describe_log_groups")
1102
1100
  for page in paginator.paginate():
1103
- for log_group in page["logGroups"]:
1104
- yield log_group
1101
+ yield from page["logGroups"]
1105
1102
 
1106
1103
  def get_cloudwatch_log_group_tags(
1107
1104
  self,
@@ -1175,7 +1172,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1175
1172
 
1176
1173
  @staticmethod
1177
1174
  # pylint: disable=method-hidden
1178
- def get_vpc_default_sg_id(vpc_id: str, ec2: EC2Client) -> Optional[str]:
1175
+ def get_vpc_default_sg_id(vpc_id: str, ec2: EC2Client) -> str | None:
1179
1176
  vpc_security_groups = ec2.describe_security_groups(
1180
1177
  Filters=[
1181
1178
  {"Name": "vpc-id", "Values": [vpc_id]},
@@ -1196,7 +1193,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1196
1193
 
1197
1194
  def get_tgw_default_route_table_id(
1198
1195
  self, ec2: EC2Client, tgw_id: str, tags: Mapping[str, str]
1199
- ) -> Optional[str]:
1196
+ ) -> str | None:
1200
1197
  tgws = self.get_transit_gateways(ec2)
1201
1198
  tgws = self.filter_on_tags(tgws, tags)
1202
1199
  # we know the party TGW exists, so we can be
@@ -1555,7 +1552,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1555
1552
 
1556
1553
  def get_image_id(
1557
1554
  self, account_name: str, region_name: str, tags: Iterable[AmiTag]
1558
- ) -> Optional[str]:
1555
+ ) -> str | None:
1559
1556
  """
1560
1557
  Get AMI ID matching the specified criteria.
1561
1558
 
@@ -1584,7 +1581,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1584
1581
  self,
1585
1582
  account_name: str,
1586
1583
  db_instance_name: str,
1587
- region_name: Optional[str] = None,
1584
+ region_name: str | None = None,
1588
1585
  ) -> DBInstanceMessageTypeDef:
1589
1586
  """
1590
1587
  Describe a single RDS instance.
@@ -1606,7 +1603,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1606
1603
  account_name: str,
1607
1604
  engine: str,
1608
1605
  engine_version: str,
1609
- region_name: Optional[str] = None,
1606
+ region_name: str | None = None,
1610
1607
  ) -> list[UpgradeTargetTypeDef]:
1611
1608
  """
1612
1609
  Get a list version of the database engine that a DB instance can be upgraded to.
@@ -1640,7 +1637,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1640
1637
  account_name: str,
1641
1638
  bucket_name: str,
1642
1639
  path: str,
1643
- region_name: Optional[str] = None,
1640
+ region_name: str | None = None,
1644
1641
  ) -> str:
1645
1642
  s3 = self._account_s3_client(account_name, region_name=region_name)
1646
1643
  return (
@@ -1648,7 +1645,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
1648
1645
  )
1649
1646
 
1650
1647
 
1651
- def aws_config_file_path() -> Optional[str]:
1648
+ def aws_config_file_path() -> str | None:
1652
1649
  config_file_path = os.path.expanduser(
1653
1650
  os.environ.get("AWS_CONFIG_FILE", "~/.aws/config")
1654
1651
  )
reconcile/utils/binary.py CHANGED
@@ -36,9 +36,7 @@ def binary_version(binary, version_args, search_regex, expected_versions):
36
36
  cmd = [binary]
37
37
  cmd.extend(version_args)
38
38
  try:
39
- result = subprocess.run(
40
- cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
41
- )
39
+ result = subprocess.run(cmd, capture_output=True, check=True)
42
40
  except subprocess.CalledProcessError as e:
43
41
  msg = (
44
42
  f"Could not execute binary '{binary}' "
@@ -1,12 +1,11 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Optional
3
2
 
4
3
  from pydantic import BaseModel
5
4
 
6
5
 
7
6
  class ClusterHealth(BaseModel):
8
7
  source: str
9
- errors: Optional[set[str]] = None
8
+ errors: set[str] | None = None
10
9
 
11
10
  def has_health_errors(self) -> bool:
12
11
  return bool(self.errors)
@@ -1,4 +1,4 @@
1
- from functools import lru_cache
1
+ from functools import cache
2
2
 
3
3
  from reconcile.utils.clusterhealth.providerbase import (
4
4
  ClusterHealth,
@@ -14,7 +14,7 @@ class TelemeterClusterHealthProvider(ClusterHealthProvider):
14
14
  def __init__(self, querier: PrometheusQuerier):
15
15
  self.querier = querier
16
16
 
17
- @lru_cache(maxsize=None)
17
+ @cache
18
18
  def cluster_health_for_org(self, org_id: str) -> dict[str, ClusterHealth]:
19
19
  vectors_by_cluster = group_by(
20
20
  self.querier.instant_vector_query(telemeter_alert_query(org_id)),
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  from typing import (
3
3
  Any,
4
- Optional,
5
4
  Self,
6
5
  )
7
6
 
@@ -27,7 +26,7 @@ class Snitch(BaseModel):
27
26
  interval: str
28
27
  alert_type: str
29
28
  alert_email: list[str]
30
- vault_data: Optional[str]
29
+ vault_data: str | None
31
30
 
32
31
  def needs_vault_update(self) -> bool:
33
32
  return self.vault_data is not None and self.check_in_url != self.vault_data
@@ -1,26 +1,24 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import (
3
3
  Any,
4
- Optional,
5
4
  Protocol,
6
- Union,
7
5
  runtime_checkable,
8
6
  )
9
7
 
10
8
 
11
9
  class HasIntegrations(Protocol):
12
- integrations: Optional[list[str]]
10
+ integrations: list[str] | None
13
11
 
14
12
 
15
13
  @runtime_checkable
16
14
  class HasDisableIntegrations(Protocol):
17
15
  @property
18
- def disable(self) -> Optional[HasIntegrations]:
16
+ def disable(self) -> HasIntegrations | None:
19
17
  pass
20
18
 
21
19
 
22
20
  def disabled_integrations(
23
- disable_obj: Optional[Union[Mapping[str, Any], HasDisableIntegrations]],
21
+ disable_obj: Mapping[str, Any] | HasDisableIntegrations | None,
24
22
  ) -> list[str]:
25
23
  """Returns all disabled integrations"""
26
24
  if not disable_obj:
@@ -38,7 +36,7 @@ def disabled_integrations(
38
36
 
39
37
  def integration_is_enabled(
40
38
  integration: str,
41
- disable_obj: Optional[Union[Mapping[str, Any], HasDisableIntegrations]],
39
+ disable_obj: Mapping[str, Any] | HasDisableIntegrations | None,
42
40
  ) -> bool:
43
41
  """A convenient method to check whether an integration is enabled or not."""
44
42
  return integration not in disabled_integrations(disable_obj)
@@ -12,7 +12,7 @@ def environ(variables=None):
12
12
  def f_environ(*args, **kwargs):
13
13
  for e in variables:
14
14
  if not os.environ.get(e):
15
- raise KeyError("Could not find environment variable: {}".format(e))
15
+ raise KeyError(f"Could not find environment variable: {e}")
16
16
  f(*args, **kwargs)
17
17
 
18
18
  return f_environ
@@ -1,10 +1,8 @@
1
1
  import datetime
2
2
  from collections.abc import Iterable
3
3
  from typing import (
4
- Optional,
5
4
  Protocol,
6
5
  TypeVar,
7
- Union,
8
6
  cast,
9
7
  )
10
8
 
@@ -12,12 +10,10 @@ DATE_FORMAT = "%Y-%m-%d"
12
10
 
13
11
 
14
12
  class FilterableRole(Protocol):
15
- expiration_date: Optional[str]
13
+ expiration_date: str | None
16
14
 
17
15
 
18
- DictsOrRoles = TypeVar(
19
- "DictsOrRoles", bound=Union[Iterable[FilterableRole], Iterable[dict]]
20
- )
16
+ DictsOrRoles = TypeVar("DictsOrRoles", bound=Iterable[FilterableRole] | Iterable[dict])
21
17
 
22
18
 
23
19
  def date_expired(date: str) -> bool:
@@ -26,7 +22,7 @@ def date_expired(date: str) -> bool:
26
22
  return current_date >= exp_date
27
23
 
28
24
 
29
- def filter(roles: Optional[DictsOrRoles]) -> DictsOrRoles:
25
+ def filter(roles: DictsOrRoles | None) -> DictsOrRoles:
30
26
  """Filters roles and returns the ones which are not yet expired."""
31
27
  filtered = []
32
28
  for r in roles or []:
@@ -7,7 +7,6 @@ from collections.abc import (
7
7
  from dataclasses import field
8
8
  from typing import (
9
9
  Any,
10
- Optional,
11
10
  cast,
12
11
  )
13
12
 
@@ -52,7 +51,7 @@ class OutputFormatProcessor:
52
51
 
53
52
  @dataclass
54
53
  class GenericSecretOutputFormatConfig(OutputFormatProcessor):
55
- data: Optional[str] = None
54
+ data: str | None = None
56
55
 
57
56
  def render(self, vars: Mapping[str, str]) -> dict[str, str]:
58
57
  if self.data:
@@ -68,7 +67,7 @@ class GenericSecretOutputFormatConfig(OutputFormatProcessor):
68
67
  @dataclass
69
68
  class OutputFormat:
70
69
  provider: str
71
- data: Optional[str] = None
70
+ data: str | None = None
72
71
 
73
72
  @property
74
73
  def _formatter(self) -> OutputFormatProcessor:
@@ -144,7 +143,7 @@ class ExternalResourceSpec:
144
143
  "app": self.namespace["app"]["name"],
145
144
  }
146
145
 
147
- def get_secret_field(self, field: str) -> Optional[str]:
146
+ def get_secret_field(self, field: str) -> str | None:
148
147
  return self.secret.get(field)
149
148
 
150
149
  def id_object(self) -> "ExternalResourceUniqueKey":
@@ -4,10 +4,7 @@ from collections.abc import (
4
4
  Mapping,
5
5
  MutableMapping,
6
6
  )
7
- from typing import (
8
- Any,
9
- Optional,
10
- )
7
+ from typing import Any
11
8
 
12
9
  import anymarkup
13
10
 
@@ -27,7 +24,7 @@ PROVIDER_CLOUDFLARE = "cloudflare"
27
24
 
28
25
 
29
26
  def get_external_resource_specs(
30
- namespace_info: Mapping[str, Any], provision_provider: Optional[str] = None
27
+ namespace_info: Mapping[str, Any], provision_provider: str | None = None
31
28
  ) -> list[ExternalResourceSpec]:
32
29
  specs: list[ExternalResourceSpec] = []
33
30
  if not managed_external_resources(namespace_info):
@@ -121,7 +118,7 @@ class ResourceValueResolver:
121
118
  def __init__(
122
119
  self,
123
120
  spec: ExternalResourceSpec,
124
- integration_tag: Optional[str] = None,
121
+ integration_tag: str | None = None,
125
122
  identifier_as_value: bool = False,
126
123
  ):
127
124
  """
@@ -207,7 +204,7 @@ class ResourceValueResolver:
207
204
 
208
205
  @staticmethod
209
206
  def _override_values(
210
- values: MutableMapping[str, Any], overrides: Optional[str]
207
+ values: MutableMapping[str, Any], overrides: str | None
211
208
  ) -> None:
212
209
  if overrides is None:
213
210
  return
@@ -1,5 +1,4 @@
1
1
  from typing import (
2
- Optional,
3
2
  TypeVar,
4
3
  )
5
4
 
@@ -8,7 +7,7 @@ ValueType = TypeVar("ValueType")
8
7
 
9
8
 
10
9
  def remove_none_values_from_dict(
11
- a_dict: dict[KeyType, Optional[ValueType]],
10
+ a_dict: dict[KeyType, ValueType | None],
12
11
  ) -> dict[KeyType, ValueType]:
13
12
  """
14
13
  Creates a new dictionary based on the input dictionary but skips items
reconcile/utils/git.py CHANGED
@@ -14,18 +14,14 @@ def clone(repo_url, wd, depth=None, verify=True):
14
14
  if depth:
15
15
  cmd += ["--depth", str(depth)]
16
16
  cmd += [repo_url, wd]
17
- result = subprocess.run(
18
- cmd, cwd=wd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
19
- )
17
+ result = subprocess.run(cmd, cwd=wd, capture_output=True, check=False)
20
18
  if result.returncode != 0:
21
19
  raise GitError(f"git clone failed: {repo_url}")
22
20
 
23
21
 
24
22
  def checkout(commit, wd):
25
23
  cmd = ["git", "checkout", commit]
26
- result = subprocess.run(
27
- cmd, cwd=wd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
28
- )
24
+ result = subprocess.run(cmd, cwd=wd, capture_output=True, check=False)
29
25
  if result.returncode != 0:
30
26
  raise GitError(f"git checkout failed: {commit}")
31
27
 
@@ -34,9 +30,7 @@ def is_file_in_git_repo(file_path):
34
30
  real_path = os.path.realpath(file_path)
35
31
  dir_path = os.path.dirname(real_path)
36
32
  cmd = ["git", "rev-parse", "--is-inside-work-tree"]
37
- result = subprocess.run(
38
- cmd, cwd=dir_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
39
- )
33
+ result = subprocess.run(cmd, cwd=dir_path, capture_output=True, check=False)
40
34
  return result.returncode == 0
41
35
 
42
36
 
@@ -11,9 +11,9 @@ from reconcile.utils import git
11
11
 
12
12
  @retry()
13
13
  def scan_history(repo_url, existing_keys):
14
- logging.info("scanning {}".format(repo_url))
14
+ logging.info(f"scanning {repo_url}")
15
15
  if requests.get(repo_url, timeout=60).status_code == 404:
16
- logging.info("not found {}".format(repo_url))
16
+ logging.info(f"not found {repo_url}")
17
17
  return []
18
18
 
19
19
  with tempfile.TemporaryDirectory() as wd:
@@ -27,11 +27,11 @@ def scan_history(repo_url, existing_keys):
27
27
  )
28
28
  if result.returncode == 0:
29
29
  return []
30
- logging.info("found suspects in {}".format(repo_url))
30
+ logging.info(f"found suspects in {repo_url}")
31
31
  suspected_files = get_suspected_files(result.stderr.decode("utf-8"))
32
32
  leaked_keys = get_leaked_keys(wd, suspected_files, existing_keys)
33
33
  if leaked_keys:
34
- logging.info("found suspected leaked keys: {}".format(leaked_keys))
34
+ logging.info(f"found suspected leaked keys: {leaked_keys}")
35
35
  return leaked_keys
36
36
 
37
37
 
@@ -55,7 +55,7 @@ def get_leaked_keys(repo_wd, suspected_files, existing_keys):
55
55
  commit, file_relative_path = s[0], s[1]
56
56
  git.checkout(commit, repo_wd)
57
57
  file_path = os.path.join(repo_wd, file_relative_path)
58
- with open(file_path, "r", encoding="locale") as f:
58
+ with open(file_path, encoding="locale") as f:
59
59
  content = f.read()
60
60
  leaked_keys = [key for key in existing_keys if key in content]
61
61
  all_leaked_keys.extend(leaked_keys)
@@ -1,10 +1,6 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
  from types import TracebackType
4
- from typing import (
5
- Optional,
6
- Type,
7
- )
8
4
  from urllib.parse import urlparse
9
5
 
10
6
  from github import (
@@ -33,7 +29,7 @@ class GithubRepositoryApi:
33
29
  repo_url: str,
34
30
  token: str,
35
31
  timeout: int = 30,
36
- github: Optional[Github] = None,
32
+ github: Github | None = None,
37
33
  ):
38
34
  parsed_repo_url = urlparse(repo_url)
39
35
  repo = parsed_repo_url.path.strip("/")
@@ -48,9 +44,9 @@ class GithubRepositoryApi:
48
44
 
49
45
  def __exit__(
50
46
  self,
51
- exc_type: Optional[Type[BaseException]],
52
- exc_value: Optional[BaseException],
53
- traceback: Optional[TracebackType],
47
+ exc_type: type[BaseException] | None,
48
+ exc_value: BaseException | None,
49
+ traceback: TracebackType | None,
54
50
  ) -> None:
55
51
  self.cleanup()
56
52
 
@@ -67,7 +63,7 @@ class GithubRepositoryApi:
67
63
  return tree_items
68
64
 
69
65
  @retry()
70
- def get_file(self, path: str, ref: str = "master") -> Optional[bytes]:
66
+ def get_file(self, path: str, ref: str = "master") -> bytes | None:
71
67
  try:
72
68
  content = self._repo.get_contents(path=path, ref=ref)
73
69
  if isinstance(content, list):
@@ -12,7 +12,6 @@ from operator import (
12
12
  )
13
13
  from typing import (
14
14
  Any,
15
- Optional,
16
15
  TypedDict,
17
16
  )
18
17
  from urllib.parse import urlparse
@@ -798,7 +797,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
798
797
  author, assignee = last_assignment[0], last_assignment[1]
799
798
  return author in team_usernames and mr.assignee["username"] == assignee
800
799
 
801
- def last_assignment(self, mr: ProjectMergeRequest) -> Optional[tuple[str, str]]:
800
+ def last_assignment(self, mr: ProjectMergeRequest) -> tuple[str, str] | None:
802
801
  body_format = "assigned to @"
803
802
  notes = self.get_items(mr.notes.list)
804
803
 
@@ -817,7 +816,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
817
816
 
818
817
  def last_comment(
819
818
  self, mr: ProjectMergeRequest, exclude_bot=True
820
- ) -> Optional[dict[str, Any]]:
819
+ ) -> dict[str, Any] | None:
821
820
  comments = self.get_merge_request_comments(mr)
822
821
  comments.sort(key=itemgetter("created_at"), reverse=True)
823
822
  for comment in comments: