qontract-reconcile 0.10.2.dev296__py3-none-any.whl → 0.10.2.dev297__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.dev296
3
+ Version: 0.10.2.dev297
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
@@ -82,12 +82,12 @@ reconcile/openshift_tekton_resources.py,sha256=z9OidaI7Ju2O0O0PfIcdoyH62VA4TxLTD
82
82
  reconcile/openshift_upgrade_watcher.py,sha256=dGTQQdCOl95Bz0wOqg6JaMdBSSDgnAveH_hprUafhW0,6624
83
83
  reconcile/openshift_users.py,sha256=h4dH3gTCFFQID76PFuYeMWNzFQ9DgTUtsOcvxfj-3cs,5385
84
84
  reconcile/openshift_vault_secrets.py,sha256=Ax-_EBWWU1VRHYyKaUkGJkIjHGwWM3bZgjXL5CkPW8k,1883
85
- reconcile/quay_base.py,sha256=GQkUpuEzC1V_QrHEu_PoELVGnlNRvgFnTqVKp-xkgC4,2070
85
+ reconcile/quay_base.py,sha256=hfHv8ET6iw0GqPyncYJMRH7YFwJc5E1C9z7zET5MCjo,2327
86
86
  reconcile/quay_membership.py,sha256=No2sgEyTVj-hr5VPLy_xdrYAPvt-xo-CPpOt0X3x_6o,6623
87
87
  reconcile/quay_mirror.py,sha256=pA1_OujRduwQ6dYljoWXU_VJgAwlv7DzThk26ymKmGs,14327
88
- reconcile/quay_mirror_org.py,sha256=2xLD-PZggP33LhZYxun5I3deF8hwGH9zueMtAByphzE,10842
88
+ reconcile/quay_mirror_org.py,sha256=ltPbHuWUI8Wnl8gV4aeYmvoYFA1uXLWqlXqEPpw7Hi0,11065
89
89
  reconcile/quay_permissions.py,sha256=BF539lRxjpgwm88WzazklzgaCF_ipRALwbO2AdpqUqE,4388
90
- reconcile/quay_repos.py,sha256=woB2afCBgz0UPekHcYtV8zwQCZHZZBL8VDf82ATWZxE,7524
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
93
  reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
@@ -637,11 +637,11 @@ reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,36
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
640
- reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
640
+ reconcile/utils/password_validator.py,sha256=knR6jJGc-v44v-hhQFvpYrEubuFfCCc3Qly6n_GJm5I,2191
641
641
  reconcile/utils/prometheus.py,sha256=Ad0rwLbxRuuYjHwkwJloHEdK0bvy42h-p-HIT1DhDhs,3832
642
642
  reconcile/utils/promotion_state.py,sha256=McSgGj3oog83ThJCrMR2v8q6Xb_Pxij-HEe_RbDu8cg,3946
643
643
  reconcile/utils/promtool.py,sha256=YnqwMAzsQVGuBZ1j9zy3UcVPFQVJgBMLzQkxhK_KFkU,3079
644
- reconcile/utils/quay_api.py,sha256=Z6e3oPSp8sCIYxUSDTc6r_qZmXN8Ci_gK90DSCF7BDw,7814
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
647
  reconcile/utils/repo_owners.py,sha256=P0QX6F0oB8wYA08yiyzhYUiBtU57iIK_PsxbzKENbKM,6571
@@ -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=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
799
- qontract_reconcile-0.10.2.dev296.dist-info/METADATA,sha256=7dJrZxkVMSto5PnvpCwOcPohimXhWm9frSgZUDnUXdw,24916
800
- qontract_reconcile-0.10.2.dev296.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
- qontract_reconcile-0.10.2.dev296.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
- qontract_reconcile-0.10.2.dev296.dist-info/RECORD,,
799
+ qontract_reconcile-0.10.2.dev297.dist-info/METADATA,sha256=iGF5qMFr_OjJTe4VrXqoImqpXd6uvpYs0wXPJhdp1q0,24916
800
+ qontract_reconcile-0.10.2.dev297.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
+ qontract_reconcile-0.10.2.dev297.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
+ qontract_reconcile-0.10.2.dev297.dist-info/RECORD,,
reconcile/quay_base.py CHANGED
@@ -1,12 +1,24 @@
1
1
  from collections import namedtuple
2
- from typing import Any
2
+ from typing import Any, TypedDict
3
3
 
4
4
  from reconcile import queries
5
5
  from reconcile.utils.quay_api import QuayApi
6
6
  from reconcile.utils.secret_reader import SecretReader
7
7
 
8
8
  OrgKey = namedtuple("OrgKey", ["instance", "org_name"])
9
- QuayApiStore = dict[OrgKey, dict[str, Any]]
9
+
10
+
11
+ class OrgInfo(TypedDict):
12
+ url: str
13
+ api: QuayApi
14
+ push_token: dict[str, str] | None
15
+ teams: list[str]
16
+ managedRepos: bool
17
+ mirror: OrgKey | None
18
+ mirror_filters: dict[str, Any]
19
+
20
+
21
+ QuayApiStore = dict[OrgKey, OrgInfo]
10
22
 
11
23
 
12
24
  def get_quay_api_store() -> QuayApiStore:
@@ -49,14 +61,16 @@ def get_quay_api_store() -> QuayApiStore:
49
61
  else:
50
62
  push_token = None
51
63
 
52
- store[org_key] = {
64
+ org_info: OrgInfo = {
53
65
  "url": base_url,
54
66
  "api": QuayApi(token, org_name, base_url=base_url),
55
67
  "push_token": push_token,
56
- "teams": org_data.get("managedTeams"),
57
- "managedRepos": org_data.get("managedRepos"),
68
+ "teams": org_data.get("managedTeams") or [],
69
+ "managedRepos": bool(org_data.get("managedRepos")),
58
70
  "mirror": mirror,
59
71
  "mirror_filters": mirror_filters,
60
72
  }
61
73
 
74
+ store[org_key] = org_info
75
+
62
76
  return store
@@ -103,11 +103,16 @@ class QuayMirrorOrg:
103
103
 
104
104
  quay_api = org_info["api"]
105
105
  upstream_org_key = org_info["mirror"]
106
+ assert upstream_org_key is not None
106
107
  upstream_org = self.quay_api_store[upstream_org_key]
107
108
  upstream_quay_api = upstream_org["api"]
108
109
 
109
- username = upstream_org["push_token"]["user"]
110
- token = upstream_org["push_token"]["token"]
110
+ push_token = upstream_org["push_token"]
111
+
112
+ assert push_token is not None
113
+
114
+ username = push_token["user"]
115
+ token = push_token["token"]
111
116
 
112
117
  org_repos = [item["name"] for item in quay_api.list_images()]
113
118
  for repo in upstream_quay_api.list_images():
@@ -143,8 +148,11 @@ class QuayMirrorOrg:
143
148
  org_name = org_key.org_name
144
149
 
145
150
  server_url = org["url"]
146
- username = org["push_token"]["user"]
147
- password = org["push_token"]["token"]
151
+ push_token = org["push_token"]
152
+ assert push_token is not None
153
+
154
+ username = push_token["user"]
155
+ password = push_token["token"]
148
156
 
149
157
  for item in data:
150
158
  image = Image(
@@ -281,6 +289,8 @@ class QuayMirrorOrg:
281
289
  """
282
290
 
283
291
  push_token = self.quay_api_store[org_key]["push_token"]
292
+ assert push_token is not None
293
+
284
294
  username = push_token["user"]
285
295
  password = push_token["token"]
286
296
  return f"{username}:{password}"
reconcile/quay_repos.py CHANGED
@@ -230,6 +230,7 @@ def run(dry_run: bool) -> None:
230
230
  if org_info.get("mirror"):
231
231
  # ensure there are no circular mirror dependencies
232
232
  mirror_org_key = org_info["mirror"]
233
+ assert mirror_org_key is not None
233
234
  mirror_org = quay_api_store[mirror_org_key]
234
235
  if mirror_org.get("mirror"):
235
236
  logging.error(
@@ -29,7 +29,7 @@ class PasswordValidator:
29
29
  self._policy_flags = policy_flags
30
30
  self._mininum_length = minimum_length
31
31
 
32
- def validate(self, password: str):
32
+ def validate(self, password: str) -> None:
33
33
  errors: list[str] = []
34
34
 
35
35
  if len(password) < self._mininum_length:
@@ -1,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  import requests
2
4
 
3
5
 
@@ -8,17 +10,23 @@ class QuayTeamNotFoundError(Exception):
8
10
  class QuayApi:
9
11
  LIMIT_FOLLOWS = 15
10
12
 
11
- def __init__(self, token, organization, base_url="quay.io", timeout=60):
13
+ def __init__(
14
+ self,
15
+ token: str,
16
+ organization: str,
17
+ base_url: str = "quay.io",
18
+ timeout: int = 60,
19
+ ) -> None:
12
20
  self.token = token
13
21
  self.organization = organization
14
22
  self.auth_header = {"Authorization": "Bearer %s" % (token,)}
15
- self.team_members = {}
23
+ self.team_members: dict[str, Any] = {}
16
24
  self.api_url = f"https://{base_url}/api/v1"
17
25
 
18
26
  self._timeout = timeout
19
27
  """Timeout to use for HTTP calls to Quay (seconds)."""
20
28
 
21
- def list_team_members(self, team, **kwargs):
29
+ def list_team_members(self, team: str, **kwargs: Any) -> list[dict]:
22
30
  """
23
31
  List Quay team members.
24
32
 
@@ -52,12 +60,12 @@ class QuayApi:
52
60
 
53
61
  return members_list
54
62
 
55
- def user_exists(self, user):
63
+ def user_exists(self, user: str) -> bool:
56
64
  url = f"{self.api_url}/users/{user}"
57
65
  r = requests.get(url, headers=self.auth_header, timeout=self._timeout)
58
66
  return r.ok
59
67
 
60
- def remove_user_from_team(self, user, team):
68
+ def remove_user_from_team(self, user: str, team: str) -> bool:
61
69
  """Deletes an user from a team.
62
70
 
63
71
  :raises HTTPError if there are any problems with the request
@@ -80,7 +88,7 @@ class QuayApi:
80
88
 
81
89
  return True
82
90
 
83
- def add_user_to_team(self, user, team):
91
+ def add_user_to_team(self, user: str, team: str) -> bool:
84
92
  """Adds an user to a team.
85
93
 
86
94
  :raises HTTPError if there are any errors with the request
@@ -93,7 +101,9 @@ class QuayApi:
93
101
  r.raise_for_status()
94
102
  return True
95
103
 
96
- def create_or_update_team(self, team: str, role="member", description=None) -> None:
104
+ def create_or_update_team(
105
+ self, team: str, role: str = "member", description: str | None = None
106
+ ) -> None:
97
107
  """
98
108
  Create or update an Organization team.
99
109
 
@@ -117,7 +127,9 @@ class QuayApi:
117
127
  )
118
128
  r.raise_for_status()
119
129
 
120
- def list_images(self, images=None, page=None, count=0):
130
+ def list_images(
131
+ self, images: list | None = None, page: str | None = None, count: int = 0
132
+ ) -> list[dict[str, Any]]:
121
133
  """
122
134
  https://docs.quay.io/api/swagger/#!/repository/listRepos
123
135
 
@@ -156,7 +168,7 @@ class QuayApi:
156
168
  return self.list_images(images, next_page, count + 1)
157
169
  return images
158
170
 
159
- def repo_create(self, repo_name, description, public):
171
+ def repo_create(self, repo_name: str, description: str, public: str) -> None:
160
172
  """Creates a repository called repo_name with the given description
161
173
  and public flag.
162
174
 
@@ -180,14 +192,14 @@ class QuayApi:
180
192
  )
181
193
  r.raise_for_status()
182
194
 
183
- def repo_delete(self, repo_name):
195
+ def repo_delete(self, repo_name: str) -> None:
184
196
  url = f"{self.api_url}/repository/{self.organization}/{repo_name}"
185
197
 
186
198
  # perform request
187
199
  r = requests.delete(url, headers=self.auth_header, timeout=self._timeout)
188
200
  r.raise_for_status()
189
201
 
190
- def repo_update_description(self, repo_name, description):
202
+ def repo_update_description(self, repo_name: str, description: str) -> None:
191
203
  url = f"{self.api_url}/repository/{self.organization}/{repo_name}"
192
204
 
193
205
  params = {"description": description}
@@ -198,13 +210,13 @@ class QuayApi:
198
210
  )
199
211
  r.raise_for_status()
200
212
 
201
- def repo_make_public(self, repo_name):
213
+ def repo_make_public(self, repo_name: str) -> None:
202
214
  self._repo_change_visibility(repo_name, "public")
203
215
 
204
- def repo_make_private(self, repo_name):
216
+ def repo_make_private(self, repo_name: str) -> None:
205
217
  self._repo_change_visibility(repo_name, "private")
206
218
 
207
- def _repo_change_visibility(self, repo_name, visibility):
219
+ def _repo_change_visibility(self, repo_name: str, visibility: str) -> None:
208
220
  url = f"{self.api_url}/repository/{self.organization}/{repo_name}/changevisibility"
209
221
 
210
222
  params = {"visibility": visibility}
@@ -215,7 +227,7 @@ class QuayApi:
215
227
  )
216
228
  r.raise_for_status()
217
229
 
218
- def get_repo_team_permissions(self, repo_name, team):
230
+ def get_repo_team_permissions(self, repo_name: str, team: str) -> str | None:
219
231
  url = (
220
232
  f"{self.api_url}/repository/{self.organization}/"
221
233
  + f"{repo_name}/permissions/team/{team}"
@@ -231,7 +243,7 @@ class QuayApi:
231
243
 
232
244
  return r.json().get("role") or None
233
245
 
234
- def set_repo_team_permissions(self, repo_name, team, role):
246
+ def set_repo_team_permissions(self, repo_name: str, team: str, role: str) -> None:
235
247
  url = (
236
248
  f"{self.api_url}/repository/{self.organization}/"
237
249
  + f"{repo_name}/permissions/team/{team}"