qontract-reconcile 0.10.2.dev313__py3-none-any.whl → 0.10.2.dev315__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev313
3
+ Version: 0.10.2.dev315
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -90,27 +90,27 @@ reconcile/quay_permissions.py,sha256=BF539lRxjpgwm88WzazklzgaCF_ipRALwbO2AdpqUqE
90
90
  reconcile/quay_repos.py,sha256=fBleLzMtfDmTidpzbrTt8kGCy-Bk3J06EO4hhyghGnQ,7570
91
91
  reconcile/queries.py,sha256=FLUZBtFC2S-e6yjtC1Oq968CJP6t3nc36NPj0wYa0bE,53472
92
92
  reconcile/query_validator.py,sha256=csOSkKxcf6ZlpchJu4ck2jLYKUN6y1l-UmSQUFHgssY,1618
93
- reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
93
+ reconcile/requests_sender.py,sha256=g-tlrudvIqhneQPDMrfYF0Xsq7BSW2QcBPirl7hFM6I,4058
94
94
  reconcile/resource_scraper.py,sha256=sg10j7lwAE8JxsyBTaxixOR3QYnePctsNuwOLiz4QVg,2309
95
95
  reconcile/resource_template_tester.py,sha256=DsKvBuNLPxm4Fa-e1YHHySnhThm5i_j-nF3G4b02Mz0,2416
96
96
  reconcile/run_integration.py,sha256=M2rIpcx7k0vGkTHdgm0h1efLOWiJ-O2WbYPHKtvIpWA,10214
97
97
  reconcile/saas_file_validator.py,sha256=wmO7yni6zCiQN2micOQ1HdA0-3-N3IvBbBYLLzbNvsI,2499
98
- reconcile/sendgrid_teammates.py,sha256=oO8QbLb4s1o8A6CGiCagN9CmS05BSS_WLztuY0Ym9D8,4773
98
+ reconcile/sendgrid_teammates.py,sha256=rknegYin7214pfqXmCALh8cd8n_qha18fGEr4_1ZFOQ,5183
99
99
  reconcile/service_dependencies.py,sha256=KydjllianL0lUwhroBAPui7jrnhHSq7j2PB6CoV2nuU,4349
100
100
  reconcile/signalfx_endpoint_monitoring.py,sha256=Nqgsg1cflSd2nNnm89y_e8c--7xLUqTrKOHkDs-qADE,2868
101
101
  reconcile/slack_base.py,sha256=I-msunWxfgu5bSwXYulGbtLjxUB_tRmTCAUCU-3nabI,3484
102
102
  reconcile/slack_usergroups.py,sha256=3uQVZK0WeZfvE1g7xQwciKCcC3LifDa3NuE1ygQ0cRk,30174
103
103
  reconcile/sql_query.py,sha256=FVwANLPWjkUHqN2OXJ-vnX5hqqcO6rTdyLEO4HkmAgM,26397
104
- reconcile/status.py,sha256=cY4IJFXemhxptRJqR4qaaOWqei9e4jgLXuVSGajMsjg,544
104
+ reconcile/status.py,sha256=h73oaSxO5sftEGqFrXVAlySBvNYPWpowDaYYplcky5A,565
105
105
  reconcile/status_board.py,sha256=0vTQzxrFPTmJtzNOC-iaJG_BmXbDe2vgBUe0LMUyfDE,15313
106
- reconcile/terraform_aws_route53.py,sha256=CWp5bE3ddUrJGNNvG8YmkSPyNHCWtOc1GEDVLnbOY9Q,10043
106
+ reconcile/terraform_aws_route53.py,sha256=p_NZDjZzOJSgFTY7KJ1qHERWaTGvFJSykY8uIYgSmVg,10183
107
107
  reconcile/terraform_cloudflare_dns.py,sha256=0Eu46o_BBEEq-B-CCvKop9VTbwrvliCKGSS9gLBSJE4,13456
108
- reconcile/terraform_cloudflare_resources.py,sha256=tK-BxQeNdZjf59deKd51Roz868e7UXe52XvcHsffJK0,14982
108
+ reconcile/terraform_cloudflare_resources.py,sha256=HlctpWi-9NxtsO3hL9fpPpD0m5v5bgL0LRQsp7ToeAs,15047
109
109
  reconcile/terraform_cloudflare_users.py,sha256=mlSYNktRetBvw8mi2TUSdKSZw0aQ821VeU6OQ1WcV3U,13516
110
110
  reconcile/terraform_repo.py,sha256=vVJfaCV9775FGMMTHfoobaPetSlJMiQ4arNudL2pvh8,15607
111
111
  reconcile/terraform_resources.py,sha256=AXO3_Ehcg3I6ao7qiKzXC4Mk6BqwMoNooXU50c2zSTA,19555
112
112
  reconcile/terraform_tgw_attachments.py,sha256=P2HivCjT5AlyODy-fu1qAK5355nDEArE8E4NQlIlF7U,18933
113
- reconcile/terraform_users.py,sha256=lzLco8t0_XQ4gWkZdvfOJIckDQpDUqTKqxGcT2P3W_k,10334
113
+ reconcile/terraform_users.py,sha256=xa-UK2iTy0HRFAGfFQvxTbBaypsqx3O0XGVWIU9yIxI,10659
114
114
  reconcile/terraform_vpc_peerings.py,sha256=rMotDHy3Fd5QTxrOGjCJ3f2vICk3fmxrKYVCIU8p_y4,27787
115
115
  reconcile/vault_replication.py,sha256=trtbB-jDwca822J5I_s0zlwFtlyaiAtqgbPeqp7Cggc,17714
116
116
  reconcile/vpc_peerings_validator.py,sha256=_77eu6DSy6VjTE5mhV-sOIVOGIiBvDEEDCdwwRdrgVQ,7101
@@ -633,7 +633,7 @@ reconcile/utils/oc_filters.py,sha256=Tz3OwtbUaYKmxENFls5CtPVzkZDeFXknw53dJe-wbT8
633
633
  reconcile/utils/oc_map.py,sha256=ougQ-Wlsa8ymoE_lPQ7g2LlpsUOsHVeRCLYW_6fjeWU,8976
634
634
  reconcile/utils/ocm_base_client.py,sha256=t5kxhklEqOpenXPkXiwQIk8d3D7hIUndBm5qGusS0bc,6681
635
635
  reconcile/utils/openshift_resource.py,sha256=Nbte4oCzxL27Hrt2vNs2ybV3X3pV4vGJEnErEsiA6UY,24810
636
- reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
636
+ reconcile/utils/openssl.py,sha256=qdEdSmNXDgx_hhj2psEea6O12cmn3pb4GNhQJtI5l_E,399
637
637
  reconcile/utils/output.py,sha256=wvsyf8NsFTaFHNkc8to75ta8f474Y4TMO4cHyAHEePk,2209
638
638
  reconcile/utils/pagerduty_api.py,sha256=6Ae-KjcmA6Bf328UhTdQ2VwYjh4uFIW1NdZW6PUgT-c,7607
639
639
  reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
@@ -644,17 +644,17 @@ reconcile/utils/promtool.py,sha256=YnqwMAzsQVGuBZ1j9zy3UcVPFQVJgBMLzQkxhK_KFkU,3
644
644
  reconcile/utils/quay_api.py,sha256=ZWjfjzFnIsbKRDcdAnP9tWQezclf53I7VWZJ0gbF2kE,8260
645
645
  reconcile/utils/quay_mirror.py,sha256=dpWCNv5lITwIk6Q9RkmqaQKHNk_JPy27UQEribJ7E-U,1324
646
646
  reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
647
- reconcile/utils/repo_owners.py,sha256=P0QX6F0oB8wYA08yiyzhYUiBtU57iIK_PsxbzKENbKM,6571
647
+ reconcile/utils/repo_owners.py,sha256=c6Z-U5TkiRPvuhr_zYWvZG9HZGzoT-l-d2PJ33lGflE,6507
648
648
  reconcile/utils/rest_api_base.py,sha256=MT7tp6CQO2S5aKfVOzw_hipWg7wAGoOqkm4qurI1hEU,4342
649
649
  reconcile/utils/rhcsv2_certs.py,sha256=ZnlUlEI2k6UrljGarkm1ey0znMlQtjeZB7VEfCH1A64,2545
650
650
  reconcile/utils/ruamel.py,sha256=FzL4_L0FnMOUZmgThrZSMJs5MTdXwiy-E9MZWfk8bh8,397
651
651
  reconcile/utils/secret_reader.py,sha256=vCU-g7q1Fbd5qOUMn7TAeE5yWhlmXme0OzZgyxk7tkY,10306
652
652
  reconcile/utils/semver_helper.py,sha256=-WfPOMSA2v1h7hT3PwVf-Htg7wOsoKlQC1JdmDX2Ars,1268
653
- reconcile/utils/sharding.py,sha256=CNyU9mkSbt7FRANEhOOZpWJl7L-YliHhaxr898gmX8c,573
653
+ reconcile/utils/sharding.py,sha256=_0AsDQ_fi9lYjI8MJABP1FaMjJQ3L93xBUDWx5e3DDw,586
654
654
  reconcile/utils/slack_api.py,sha256=fcqdi9cKRpibT5FlaeOY5iBQ3Dnni55u1Va9HcQC3Ls,17863
655
655
  reconcile/utils/slo_document_manager.py,sha256=YqiCYA_kRq5_oNc_VTYipjbph4k0XHDcjwbbcdaW1ag,9563
656
656
  reconcile/utils/smtp_client.py,sha256=0xefB4I9E5eBB-FlxFJYjvz3Kvuqi_K3Ma_Wk0NAQKM,2779
657
- reconcile/utils/sqs_gateway.py,sha256=XNIf3PY4UCPNufP2Ul0UJj3fKlt5larBba-VTT-41Fg,2265
657
+ reconcile/utils/sqs_gateway.py,sha256=x0dGIJeuMTsIKhHyosVPqi_POX0IJ9OWVqAHRa55dxI,2551
658
658
  reconcile/utils/state.py,sha256=vCHYIfrWLfPyIWEHSaADWlc4OqhwcOiqM3Egqvw-lfo,16372
659
659
  reconcile/utils/structs.py,sha256=P57POzpEntu8ZoZDnsOdni9qUuBDWknmw0iinznxXoY,386
660
660
  reconcile/utils/terraform_client.py,sha256=zF_F31WzDA3fjNPj_Jl6mQJMfI3wEQ0gsurtrjqh4Ds,37684
@@ -744,7 +744,7 @@ reconcile/utils/runtime/__init__.py,sha256=sfk92MGfsBh9tKYHl_FH17NdEsrGBwgDFTb7K
744
744
  reconcile/utils/runtime/desired_state_diff.py,sha256=Bw4zqel-klXCMZGqD1gyh8zkFq4h5qzv8rJn7K6WTXs,8132
745
745
  reconcile/utils/runtime/environment.py,sha256=h-CFKLK1qRl_gfOVIUwjqVNOmukIPzUG7AiqpJGrjHA,2038
746
746
  reconcile/utils/runtime/integration.py,sha256=I74KlnsNKd7Hf8rBRug6blXkGKidSDQEjqHRLAfgTXI,10973
747
- reconcile/utils/runtime/meta.py,sha256=dWdKS9eHVuowFkTK4lgXJ723vS1y9giOMzePUKnHnDI,214
747
+ reconcile/utils/runtime/meta.py,sha256=M_EOxrb0KhQA4TwpHekbog2jOZqaBPVTIijXyRuMjn0,255
748
748
  reconcile/utils/runtime/runner.py,sha256=OGT57aCwspAjdjGXtpbQnTmJeeWnRh9a2N73TLp4jnM,7880
749
749
  reconcile/utils/runtime/sharding.py,sha256=r0ieUtNed7NvknSw6qQrCkKpVXE1shuHGnfFcnpA_k4,16142
750
750
  reconcile/utils/saasherder/__init__.py,sha256=3U8plqMAPRE1kjwZ5YnIsYsggTf4_gS7flRUEuXVBAs,343
@@ -770,7 +770,7 @@ tools/app_sre_tekton_access_reporter.py,sha256=5qmkevJdlb2j_lpGC5Pu1Pmo0eomX5Zxz
770
770
  tools/app_sre_tekton_access_revalidation.py,sha256=vwL1o_j7oSTOhrHNH1znpgjA2LHGzb8yc5iG3aaY4m0,2684
771
771
  tools/glitchtip_access_reporter.py,sha256=wnaiDGW4MkYONV_erltnJ6nGkEj0kQrAiv04NNnOS0k,2859
772
772
  tools/glitchtip_access_revalidation.py,sha256=jjeLO53LTbz_LfQw3G2Cs8lVLO_6xqU39BYyTH3cEPE,2764
773
- tools/qontract_cli.py,sha256=KyK2iCSh68cPolZygQtYb18ZtjIUlFw4aZT9D5p91kw,159892
773
+ tools/qontract_cli.py,sha256=T_BEnJZt23_XeaQYZZxUvPiCAroe_RPtU43wJTPqXFg,159891
774
774
  tools/template_validation.py,sha256=Xn9X4sGFznx-rvBDnq9Kq16rfET8V3bqH1EwavsGBac,3335
775
775
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
776
776
  tools/cli_commands/container_images_report.py,sha256=8mAjCS6XR0yD7k0mfiVBlt6xbYU47q_ftdYNi5o5VKE,5566
@@ -796,7 +796,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
796
796
  tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
797
797
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
798
798
  tools/sre_checkpoints/util.py,sha256=KcYVfa3UmJHVP_ocgrKe8NkrO5IDB9aWEDydSokPcRk,975
799
- qontract_reconcile-0.10.2.dev313.dist-info/METADATA,sha256=KkW-0QYSeRGwwEnif-BFzkzPEStcexb8mN-6NV7Na34,24916
800
- qontract_reconcile-0.10.2.dev313.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
- qontract_reconcile-0.10.2.dev313.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
- qontract_reconcile-0.10.2.dev313.dist-info/RECORD,,
799
+ qontract_reconcile-0.10.2.dev315.dist-info/METADATA,sha256=AE42AC4sotdY4LzNv1wIEFqqW1Qt8h1onk91ARjO-YQ,24916
800
+ qontract_reconcile-0.10.2.dev315.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
+ qontract_reconcile-0.10.2.dev315.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
+ qontract_reconcile-0.10.2.dev315.dist-info/RECORD,,
@@ -1,6 +1,8 @@
1
1
  import logging
2
2
  import sys
3
+ from collections.abc import Callable, Mapping
3
4
  from subprocess import CalledProcessError
5
+ from typing import Any
4
6
 
5
7
  from reconcile import (
6
8
  queries,
@@ -44,7 +46,9 @@ Encrypted credentials:
44
46
  """
45
47
 
46
48
 
47
- def get_encrypted_credentials(credentials_name, user, settings):
49
+ def get_encrypted_credentials(
50
+ credentials_name: str, user: Mapping[str, Any], settings: Mapping[str, Any]
51
+ ) -> str | None:
48
52
  credentials_map = settings["credentials"]
49
53
  credentials_map_item = [c for c in credentials_map if c["name"] == credentials_name]
50
54
  if len(credentials_map_item) != 1:
@@ -59,7 +63,7 @@ def get_encrypted_credentials(credentials_name, user, settings):
59
63
 
60
64
 
61
65
  @defer
62
- def run(dry_run, defer=None):
66
+ def run(dry_run: bool, defer: Callable | None = None) -> None:
63
67
  settings = queries.get_app_interface_settings()
64
68
  vault_settings = get_app_interface_vault_settings()
65
69
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
@@ -76,7 +80,8 @@ def run(dry_run, defer=None):
76
80
  integration=QONTRACT_INTEGRATION,
77
81
  secret_reader=secret_reader,
78
82
  )
79
- defer(state.cleanup)
83
+ if defer:
84
+ defer(state.cleanup)
80
85
  credentials_requests = queries.get_credentials_requests()
81
86
 
82
87
  # validate no 2 requests have the same name
@@ -1,5 +1,7 @@
1
1
  import logging
2
2
  import sys
3
+ from collections.abc import Iterable, Mapping
4
+ from typing import Any
3
5
 
4
6
  import sendgrid
5
7
  from sretoolbox.utils import retry
@@ -17,18 +19,22 @@ class SendGridAPIError(Exception):
17
19
 
18
20
 
19
21
  class Teammate:
20
- def __init__(self, email, pending_token=None, username=None):
22
+ def __init__(
23
+ self, email: str, pending_token: str | None = None, username: str | None = None
24
+ ) -> None:
21
25
  self.email = email
22
- self.username = username or email.split("@")[0]
26
+ self.username = username or email.split("@", maxsplit=1)[0]
23
27
  self.pending_token = pending_token
24
28
 
25
29
  @property
26
- def pending(self):
30
+ def pending(self) -> bool:
27
31
  return bool(self.pending_token)
28
32
 
29
33
 
30
- def fetch_desired_state(users):
31
- desired_state = {}
34
+ def fetch_desired_state(
35
+ users: Iterable[Mapping[str, Any]],
36
+ ) -> dict[str, list[Teammate]]:
37
+ desired_state: dict[str, list[Teammate]] = {}
32
38
  for user in users:
33
39
  roles = user.get("roles") or []
34
40
  for role in roles:
@@ -42,7 +48,7 @@ def fetch_desired_state(users):
42
48
 
43
49
 
44
50
  @retry()
45
- def fetch_current_state(sg_client):
51
+ def fetch_current_state(sg_client: sendgrid.SendGridAPIClient) -> list[Teammate]:
46
52
  state = []
47
53
  limit = 100
48
54
 
@@ -79,7 +85,7 @@ def fetch_current_state(sg_client):
79
85
  return state
80
86
 
81
87
 
82
- def raise_if_error(response):
88
+ def raise_if_error(response: Any) -> None:
83
89
  """
84
90
  Raises an SendGridAPIError if the request has returned an error
85
91
  """
@@ -87,7 +93,12 @@ def raise_if_error(response):
87
93
  raise SendGridAPIError(response.body.decode("utf-8"))
88
94
 
89
95
 
90
- def act(dry_run, sg_client, desired_state, current_state):
96
+ def act(
97
+ dry_run: bool,
98
+ sg_client: sendgrid.SendGridAPIClient,
99
+ desired_state: Iterable[Teammate],
100
+ current_state: Iterable[Teammate],
101
+ ) -> bool:
91
102
  """
92
103
  Reconciles current state with desired state.
93
104
 
@@ -145,7 +156,7 @@ def act(dry_run, sg_client, desired_state, current_state):
145
156
  return error
146
157
 
147
158
 
148
- def run(dry_run):
159
+ def run(dry_run: bool) -> None:
149
160
  settings = queries.get_app_interface_settings()
150
161
  secret_reader = SecretReader(settings=settings)
151
162
 
reconcile/status.py CHANGED
@@ -11,7 +11,7 @@ class ExitCodes:
11
11
  class _RunningState:
12
12
  _state: dict[Any, Any] = {}
13
13
 
14
- def __init__(self):
14
+ def __init__(self) -> None:
15
15
  self.__dict__ = self._state
16
16
 
17
17
 
@@ -22,7 +22,7 @@ class RunningState(_RunningState):
22
22
  by the callers.
23
23
  """
24
24
 
25
- def __getattr__(self, item):
25
+ def __getattr__(self, item: str) -> None:
26
26
  """
27
27
  Default value for attributes not explicitly created is None.
28
28
  """
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import sys
3
3
  from collections.abc import (
4
+ Callable,
4
5
  Iterable,
5
6
  Mapping,
6
7
  )
@@ -25,8 +26,10 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
25
26
 
26
27
 
27
28
  def build_desired_state(
28
- zones: Iterable[Mapping], all_accounts: Iterable[Mapping], settings: Mapping
29
- ) -> list[dict]:
29
+ zones: Iterable[Mapping[str, Any]],
30
+ all_accounts: Iterable[Mapping[str, Any]],
31
+ settings: Mapping[str, Any],
32
+ ) -> list[dict[str, Any]]:
30
33
  """
31
34
  Build the desired state from the app-interface resources
32
35
 
@@ -36,7 +39,7 @@ def build_desired_state(
36
39
  :rtype: list of dict
37
40
  """
38
41
 
39
- desired_state = []
42
+ desired_state: list[dict[str, Any]] = []
40
43
  for zone in zones:
41
44
  account = zone["account"]
42
45
  account_name = account["name"]
@@ -208,8 +211,8 @@ def run(
208
211
  enable_deletion: bool = True,
209
212
  thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
210
213
  account_name: str | None = None,
211
- defer=None,
212
- ):
214
+ defer: Callable | None = None,
215
+ ) -> None:
213
216
  settings = queries.get_app_interface_settings()
214
217
  zones = queries.get_dns_zones(account_name=account_name)
215
218
 
@@ -255,7 +258,8 @@ def run(
255
258
  if tf is None:
256
259
  sys.exit(ExitCodes.ERROR)
257
260
 
258
- defer(tf.cleanup)
261
+ if defer:
262
+ defer(tf.cleanup)
259
263
 
260
264
  _, err = tf.plan(enable_deletion)
261
265
  if err:
@@ -269,7 +273,7 @@ def run(
269
273
  sys.exit(ExitCodes.ERROR)
270
274
 
271
275
 
272
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
276
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
273
277
  return {
274
278
  "zones": queries.get_dns_zones(),
275
279
  }
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import sys
3
- from collections.abc import Iterable
3
+ from collections.abc import Callable, Iterable
4
4
  from typing import (
5
5
  Any,
6
6
  cast,
@@ -193,7 +193,7 @@ def _populate_oc_resources(
193
193
  spec: CurrentStateSpec,
194
194
  ri: ResourceInventory,
195
195
  account_names: Iterable[str] | None,
196
- ):
196
+ ) -> None:
197
197
  """
198
198
  This was taken from terraform_resources and might be a later candidate for DRY.
199
199
  """
@@ -315,7 +315,7 @@ def run(
315
315
  vault_output_path: str = "",
316
316
  internal: bool | None = None,
317
317
  use_jump_host: bool = True,
318
- defer=None,
318
+ defer: Callable | None = None,
319
319
  ) -> None:
320
320
  vault_settings = get_app_interface_vault_settings()
321
321
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
@@ -392,7 +392,8 @@ def run(
392
392
  working_dirs,
393
393
  thread_pool_size,
394
394
  )
395
- defer(tf.cleanup)
395
+ if defer:
396
+ defer(tf.cleanup)
396
397
 
397
398
  disabled_deletions_detected, err = tf.plan(enable_deletion)
398
399
  if err:
@@ -440,7 +441,7 @@ def _get_cloudflare_desired_state() -> tuple[
440
441
  return query_accounts, query_resources
441
442
 
442
443
 
443
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
444
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
444
445
  desired_state = _get_cloudflare_desired_state()
445
446
 
446
447
  return {state.__repr_name__(): state.dict() for state in desired_state}
@@ -1,4 +1,5 @@
1
1
  import sys
2
+ from collections.abc import Iterable, Mapping
2
3
  from textwrap import indent
3
4
  from typing import (
4
5
  Any,
@@ -95,9 +96,9 @@ def get_tf_roles() -> list[Role]:
95
96
 
96
97
 
97
98
  def _filter_participating_aws_accounts(
98
- accounts: list,
99
- roles: list[dict[str, Any]],
100
- ) -> list:
99
+ accounts: Iterable[dict[str, Any]],
100
+ roles: Iterable[Mapping[str, Any]],
101
+ ) -> list[dict[str, Any]]:
101
102
  participating_aws_account_names: set[str] = set()
102
103
  for role in roles:
103
104
  participating_aws_account_names.update(
@@ -111,7 +112,7 @@ def _filter_participating_aws_accounts(
111
112
 
112
113
 
113
114
  def setup(
114
- print_to_file,
115
+ print_to_file: str | None,
115
116
  thread_pool_size: int,
116
117
  skip_reencrypt_accounts: list[str],
117
118
  appsre_pgp_key: str | None = None,
@@ -146,10 +147,10 @@ def setup(
146
147
 
147
148
 
148
149
  def send_email_invites(
149
- new_users,
150
+ new_users: Iterable[tuple[str, str, str, str]],
150
151
  smtp_client: SmtpClient,
151
- skip_reencrypt_accounts: list[str],
152
- ):
152
+ skip_reencrypt_accounts: Iterable[str],
153
+ ) -> None:
153
154
  msg_template = """
154
155
  You have been invited to join the {} AWS account!
155
156
  Below you will find credentials for the first sign in.
@@ -189,9 +190,9 @@ Encrypted password: {}
189
190
  def write_user_to_vault(
190
191
  vault_client: _VaultClient,
191
192
  vault_path: str,
192
- new_users: list[tuple[str, str, str, str]],
193
- skip_reencrypt_accounts: list[str],
194
- ):
193
+ new_users: Iterable[tuple[str, str, str, str]],
194
+ skip_reencrypt_accounts: Iterable[str],
195
+ ) -> None:
195
196
  for account, console_url, user_name, enc_password in new_users:
196
197
  if account in skip_reencrypt_accounts:
197
198
  continue
@@ -208,13 +209,13 @@ def write_user_to_vault(
208
209
  vault_client.write(desired_secret, decode_base64=False)
209
210
 
210
211
 
211
- def cleanup_and_exit(tf=None, status=False):
212
+ def cleanup_and_exit(tf: Terraform | None = None, status: bool = False) -> None:
212
213
  if tf is not None:
213
214
  tf.cleanup()
214
215
  sys.exit(status)
215
216
 
216
217
 
217
- def get_reencrypt_settings():
218
+ def get_reencrypt_settings() -> tuple[list[str], str | None, Any]:
218
219
  all_reencrypt_settings = query(
219
220
  query_func=gql.get_api().query
220
221
  ).pgp_reencryption_settings
@@ -243,7 +244,7 @@ def run(
243
244
  thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
244
245
  send_mails: bool = True,
245
246
  account_name: str | None = None,
246
- ):
247
+ ) -> None:
247
248
  skip_accounts, appsre_pgp_key, reencrypt_settings = get_reencrypt_settings()
248
249
 
249
250
  # setup errors should skip resources that will lead
@@ -319,7 +320,7 @@ def run(
319
320
  cleanup_and_exit(tf, setup_err)
320
321
 
321
322
 
322
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
323
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
323
324
  """
324
325
  Finding diffs in deeply nested structures is time/resource consuming.
325
326
  Having a unique known property to identify objects makes it easier to match
@@ -330,11 +331,11 @@ def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
330
331
  for the DeepDiff library used in qontract-reconcile.
331
332
  """
332
333
 
333
- def add_account_identity(acc):
334
+ def add_account_identity(acc: dict[str, Any]) -> dict[str, Any]:
334
335
  acc[IDENTIFIER_FIELD_NAME] = acc["path"]
335
336
  return acc
336
337
 
337
- def add_role_identity(role):
338
+ def add_role_identity(role: dict[str, Any]) -> dict[str, Any]:
338
339
  role[IDENTIFIER_FIELD_NAME] = role["name"]
339
340
  return role
340
341
 
@@ -1,12 +1,12 @@
1
1
  from OpenSSL import crypto
2
2
 
3
3
 
4
- def certificate_matches_host(certificate, host):
4
+ def certificate_matches_host(certificate: bytes, host: str) -> bool:
5
5
  common_name = get_certificate_common_name(certificate)
6
6
  return host.endswith(common_name.replace("*.", ""))
7
7
 
8
8
 
9
- def get_certificate_common_name(certificate):
9
+ def get_certificate_common_name(certificate: bytes) -> str:
10
10
  cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
11
11
  subject = cert.get_subject()
12
12
  return subject.CN
@@ -1,9 +1,13 @@
1
1
  import logging
2
2
  import os
3
3
  import pathlib
4
+ from collections.abc import Iterable, Mapping
4
5
 
5
6
  from ruamel import yaml
6
7
 
8
+ from reconcile.utils.github_api import GithubRepositoryApi
9
+ from reconcile.utils.gitlab_api import GitLabApi
10
+
7
11
  _LOG = logging.getLogger(__name__)
8
12
 
9
13
 
@@ -12,38 +16,24 @@ class RepoOwners:
12
16
  Abstracts the owners of a repository with per-path granularity.
13
17
  """
14
18
 
15
- def __init__(self, git_cli, ref="master", recursive=True):
19
+ def __init__(
20
+ self,
21
+ git_cli: GitLabApi | GithubRepositoryApi,
22
+ ref: str = "master",
23
+ recursive: bool = True,
24
+ ) -> None:
16
25
  self._git_cli = git_cli
17
26
  self._ref = ref
18
- self._owners_map = None
27
+ self._owners_map: dict[str, dict[str, set[str]]] | None = None
19
28
  self._recursive = recursive
20
29
 
21
30
  @property
22
- def owners_map(self):
31
+ def owners_map(self) -> dict[str, dict[str, set[str]]]:
23
32
  if self._owners_map is None:
24
33
  self._owners_map = self._get_owners_map()
25
34
  return self._owners_map
26
35
 
27
- def get_owners(self):
28
- """
29
- Gets all the owners of the repository.
30
-
31
- :return: the repository owners
32
- :rtype: dict
33
- """
34
- repo_owners = {"approvers": set(), "reviewers": set()}
35
-
36
- if "." in self.owners_map:
37
- repo_owners["approvers"].update(self.owners_map["."]["approvers"])
38
- repo_owners["reviewers"].update(self.owners_map["."]["reviewers"])
39
-
40
- for owners in self.owners_map.values():
41
- repo_owners["approvers"].update(owners["approvers"])
42
- repo_owners["reviewers"].update(owners["reviewers"])
43
-
44
- return repo_owners
45
-
46
- def get_root_owners(self):
36
+ def get_root_owners(self) -> dict[str, list[str]]:
47
37
  """
48
38
  Gets all the owners defined in the repository root.
49
39
 
@@ -56,7 +46,7 @@ class RepoOwners:
56
46
 
57
47
  return {"approvers": [], "reviewers": []}
58
48
 
59
- def get_path_owners(self, path):
49
+ def get_path_owners(self, path: str) -> dict[str, list[str]]:
60
50
  """
61
51
  Gets all the owners of a given path, no matter in which
62
52
  level of the filesystem tree the owner was specified.
@@ -67,7 +57,7 @@ class RepoOwners:
67
57
  :return: the path owners
68
58
  :rtype: dict
69
59
  """
70
- path_owners = {"approvers": set(), "reviewers": set()}
60
+ path_owners: dict[str, set[str]] = {"approvers": set(), "reviewers": set()}
71
61
 
72
62
  if "." in self.owners_map:
73
63
  path_owners["approvers"].update(self.owners_map["."]["approvers"])
@@ -80,7 +70,7 @@ class RepoOwners:
80
70
 
81
71
  return self._set_to_sorted_list(path_owners)
82
72
 
83
- def get_path_closest_owners(self, path):
73
+ def get_path_closest_owners(self, path: str) -> dict[str, list[str]]:
84
74
  """
85
75
  Gets all closest owners of a given path, no matter in which
86
76
  level of the filesystem tree the owner was specified.
@@ -108,7 +98,7 @@ class RepoOwners:
108
98
 
109
99
  return {"approvers": [], "reviewers": []}
110
100
 
111
- def _get_owners_map(self):
101
+ def _get_owners_map(self) -> dict[str, dict[str, set[str]]]:
112
102
  """
113
103
  Maps all the OWNERS files content to their respective
114
104
  owned directory.
@@ -173,7 +163,7 @@ class RepoOwners:
173
163
  }
174
164
  return owners_map
175
165
 
176
- def _get_aliases(self):
166
+ def _get_aliases(self) -> dict[str, list[str]] | None:
177
167
  """
178
168
  Retrieves the approvers aliases from the OWNERS_ALIASES file.
179
169
 
@@ -191,7 +181,9 @@ class RepoOwners:
191
181
  return aliases["aliases"]
192
182
 
193
183
  @staticmethod
194
- def _set_to_sorted_list(owners):
184
+ def _set_to_sorted_list(
185
+ owners: Mapping[str, Iterable[str]],
186
+ ) -> dict[str, list[str]]:
195
187
  approvers = owners["approvers"]
196
188
  sorted_approvers = sorted(approvers) if approvers else []
197
189
 
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  from dataclasses import dataclass
3
+ from typing import Any
3
4
 
4
5
 
5
6
  @dataclass
@@ -8,5 +9,5 @@ class IntegrationMeta:
8
9
  args: list[str]
9
10
  short_help: str | None
10
11
 
11
- def to_dict(self):
12
+ def to_dict(self) -> dict[str, Any]:
12
13
  return dataclasses.asdict(self)
@@ -8,7 +8,7 @@ SHARDS = int(os.environ.get("SHARDS", "1"))
8
8
  SHARD_ID = int(os.environ.get("SHARD_ID", "0"))
9
9
 
10
10
 
11
- def is_in_shard(value):
11
+ def is_in_shard(value: str) -> bool:
12
12
  if SHARDS == 1:
13
13
  return True
14
14
 
@@ -1,5 +1,7 @@
1
1
  import json
2
2
  import os
3
+ from collections.abc import Iterable, Mapping
4
+ from typing import Any, Self
3
5
 
4
6
  from reconcile.utils.aws_api import AWSApi
5
7
  from reconcile.utils.secret_reader import SecretReader
@@ -12,7 +14,9 @@ class SQSGatewayInitError(Exception):
12
14
  class SQSGateway:
13
15
  """Wrapper around SQS AWS SDK"""
14
16
 
15
- def __init__(self, accounts, secret_reader: SecretReader):
17
+ def __init__(
18
+ self, accounts: Iterable[Mapping[str, Any]], secret_reader: SecretReader
19
+ ) -> None:
16
20
  queue_url = os.environ.get("gitlab_pr_submitter_queue_url") # noqa: SIM112
17
21
  if not queue_url:
18
22
  raise SQSGatewayInitError(
@@ -30,17 +34,17 @@ class SQSGateway:
30
34
  self.sqs = self._aws_api.get_session_client(session, "sqs")
31
35
  self.queue_url = queue_url
32
36
 
33
- def __enter__(self):
37
+ def __enter__(self) -> Self:
34
38
  return self
35
39
 
36
- def __exit__(self, *ext):
40
+ def __exit__(self, *ext: Any) -> None:
37
41
  self.cleanup()
38
42
 
39
- def cleanup(self):
43
+ def cleanup(self) -> None:
40
44
  self._aws_api.cleanup()
41
45
 
42
46
  @staticmethod
43
- def get_queue_account(accounts, queue_url):
47
+ def get_queue_account(accounts: Iterable[Mapping[str, Any]], queue_url: str) -> str:
44
48
  queue_account_uid = queue_url.split("/")[3]
45
49
  queue_account_name = [
46
50
  a["name"] for a in accounts if a["uid"] == queue_account_uid
@@ -49,14 +53,14 @@ class SQSGateway:
49
53
  raise SQSGatewayInitError(f"account uid not found: {queue_account_uid}")
50
54
  return queue_account_name[0]
51
55
 
52
- def send_message(self, body):
56
+ def send_message(self, body: Mapping[str, Any]) -> None:
53
57
  self.sqs.send_message(QueueUrl=self.queue_url, MessageBody=json.dumps(body))
54
58
 
55
59
  def receive_messages(
56
60
  self,
57
- visibility_timeout=30,
58
- wait_time_seconds=20,
59
- ):
61
+ visibility_timeout: int = 30,
62
+ wait_time_seconds: int = 20,
63
+ ) -> list[tuple[str, dict[str, Any]]]:
60
64
  messages = self.sqs.receive_message(
61
65
  QueueUrl=self.queue_url,
62
66
  VisibilityTimeout=visibility_timeout,
@@ -64,5 +68,5 @@ class SQSGateway:
64
68
  ).get("Messages", [])
65
69
  return [(m["ReceiptHandle"], json.loads(m["Body"])) for m in messages]
66
70
 
67
- def delete_message(self, receipt_handle):
71
+ def delete_message(self, receipt_handle: str) -> None:
68
72
  self.sqs.delete_message(QueueUrl=self.queue_url, ReceiptHandle=receipt_handle)
tools/qontract_cli.py CHANGED
@@ -1299,7 +1299,7 @@ def user_credentials_migrate_output(ctx: click.Context, account_name: str) -> No
1299
1299
  skip_accounts, appsre_pgp_key, _ = tfu.get_reencrypt_settings()
1300
1300
 
1301
1301
  accounts, working_dirs, _, aws_api = tfu.setup(
1302
- False,
1302
+ None,
1303
1303
  1,
1304
1304
  skip_accounts,
1305
1305
  account_name=account_name,