qontract-reconcile 0.10.1rc825__py3-none-any.whl → 0.10.1rc827__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.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc825
3
+ Version: 0.10.1rc827
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -35,7 +35,7 @@ reconcile/gitlab_labeler.py,sha256=IxE1XM5o4rDOFuR4cM2yAHTy4Uzdg3Nyz2mp7b8Fx1g,4
35
35
  reconcile/gitlab_members.py,sha256=M6LwFOrwgvl1NNdOJa1mrQFUon-bEVv1AyhGeLed454,8443
36
36
  reconcile/gitlab_mr_sqs_consumer.py,sha256=O46mdziPgGOndbU-0_UJKJVUaiEoVzJPEgKm4_UvYoI,2571
37
37
  reconcile/gitlab_owners.py,sha256=sn9njaKOtqcvnhi2qtm-faAfAR4zNqflbSuusA9RUuI,13456
38
- reconcile/gitlab_permissions.py,sha256=ciEKj_wnRbS_vs_ZwcUeD6HkWVe3osAuotFqJSmvd94,1638
38
+ reconcile/gitlab_permissions.py,sha256=Q_vGh8F_7-fUIDWZFigZpwkY4fvjxS7Tmq4jQCS56zw,2166
39
39
  reconcile/gitlab_projects.py,sha256=K3tFf_aD1W4Ijp5q-9Qek3kwFGEWPcZ1kd7tzFJ4GyQ,1781
40
40
  reconcile/integrations_manager.py,sha256=J_VV-HINI7YNav2NPIolePZkll-7VBuBXWAyMNhsM_Q,9535
41
41
  reconcile/jenkins_base.py,sha256=0Gocu3fU2YTltaxBlbDQOUvP-7CP2OSQV1ZRwtWeVXw,875
@@ -445,7 +445,7 @@ reconcile/statuspage/state.py,sha256=HD9EOoKm_nEqCMLIwW809En3cq5VhyzKJPUbsh-bae8
445
445
  reconcile/statuspage/status.py,sha256=mfRJ_tW7jM4_Vy_1cc8C0fKJEoA2GwrA3gJeV1KImAw,2834
446
446
  reconcile/statuspage/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
447
447
  reconcile/statuspage/integrations/components.py,sha256=49KHd_E9AdRvcEA6n75q1McZv2LfN-hRsW-WA7dgw9g,2651
448
- reconcile/statuspage/integrations/maintenances.py,sha256=7gmMS9HwORMWMMBtib6pNGLXR6sAH2aW-HXKHJn6BN4,3083
448
+ reconcile/statuspage/integrations/maintenances.py,sha256=nG45b8A_ir9Ivtr4zH_VpHMahq8wev_M5igqK6T0hP0,4225
449
449
  reconcile/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
450
450
  reconcile/templates/aws_access_key_email.j2,sha256=2MUr1ERmyISzKgHqsWYLd-1Wbl-peUa-FsGUS-JLUFc,238
451
451
  reconcile/templates/email.yml.j2,sha256=OZgczNRgXPj2gVYTgwQyHAQrMGu7xp-e4W1rX19GcrU,690
@@ -608,6 +608,7 @@ reconcile/utils/aggregated_list.py,sha256=pkYoBj7WwmaNgEefETqEOFTnQMcUzHE3mdsVdz
608
608
  reconcile/utils/amtool.py,sha256=Rg6_TTvuS7M2QbOkIjy2sRetvf6aNVdv_DA9ugSgMi4,2303
609
609
  reconcile/utils/aws_api.py,sha256=3VLg_MBih9SY9u43ZFbsaFNbDr086CJ7qQdTCfA-Rjo,66258
610
610
  reconcile/utils/aws_helper.py,sha256=MDbv5jrNdqqJ5pfBxniGdJXBBO_EYc2_Uf2w9ZzeMNs,2854
611
+ reconcile/utils/batches.py,sha256=TtEm64a8lWhFuNbUVpFEmXVdU2Q0sTBrP_I0Cjbgh7g,320
611
612
  reconcile/utils/binary.py,sha256=EsOGg82Y2QJh91SGJE0tYpBKqU0iaaagQVoYONBtQn8,2359
612
613
  reconcile/utils/config.py,sha256=aId5zrPjM_84u_T4yTRE_Psu3zo5-5_JCR6_7Wgv5UQ,990
613
614
  reconcile/utils/constants.py,sha256=pOUd97bqZdsAu5RWJ8NUs9cwCY7K9y0eW9VVeJ4fZIU,138
@@ -629,7 +630,7 @@ reconcile/utils/filtering.py,sha256=zZnHH0u0SaTDyzuFXZ_mREURGLvjEqQIQy4z-7QBVlc,
629
630
  reconcile/utils/git.py,sha256=BdxXFgQ1XOZpS-4qb3qMsKTCFDG8MlE26rv1jAhvCkM,1560
630
631
  reconcile/utils/git_secrets.py,sha256=0wGNL5mvDtVPRuu3vEQgld1Am64gIDJHtmu1_ZKxMAI,1973
631
632
  reconcile/utils/github_api.py,sha256=_bttNxYKeam_tLVe27L7O4gKqSn6CeyuFnJn8tSaUVY,2488
632
- reconcile/utils/gitlab_api.py,sha256=UMqWIHR_GkxEn5lgvxHLrjcwprRvRuKowxkwvu1xvvM,27241
633
+ reconcile/utils/gitlab_api.py,sha256=9C6oS98w1z-pDAAz5k9kgEvRO6r7hduHoOl7cufFr5s,27400
633
634
  reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
634
635
  reconcile/utils/gql.py,sha256=bzIYYYYGIO_4Db4sA-mnZUOPVNBELZD5EcIUSTbYG1o,13604
635
636
  reconcile/utils/grouping.py,sha256=kWKivD14eAkiDneH_VIl_XyUdcVVQgiaKA9sLsuD2dw,441
@@ -811,8 +812,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
811
812
  tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
812
813
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
813
814
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
814
- qontract_reconcile-0.10.1rc825.dist-info/METADATA,sha256=nGrMqUI3wHpzTSQEQryBhjGLr_ABZ4pd5zx0TOp4F_8,2314
815
- qontract_reconcile-0.10.1rc825.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
816
- qontract_reconcile-0.10.1rc825.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
817
- qontract_reconcile-0.10.1rc825.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
818
- qontract_reconcile-0.10.1rc825.dist-info/RECORD,,
815
+ qontract_reconcile-0.10.1rc827.dist-info/METADATA,sha256=u5Eopi8oHyBimPZUq-Bc7QvHZttOUWrLGXF2f7raVV4,2314
816
+ qontract_reconcile-0.10.1rc827.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
817
+ qontract_reconcile-0.10.1rc827.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
818
+ qontract_reconcile-0.10.1rc827.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
819
+ qontract_reconcile-0.10.1rc827.dist-info/RECORD,,
@@ -5,14 +5,16 @@ from typing import Any
5
5
  from sretoolbox.utils import threaded
6
6
 
7
7
  from reconcile import queries
8
+ from reconcile.utils import batches
8
9
  from reconcile.utils.defer import defer
9
10
  from reconcile.utils.gitlab_api import GitLabApi
10
11
 
11
12
  QONTRACT_INTEGRATION = "gitlab-permissions"
13
+ PAGE_SIZE = 100
12
14
 
13
15
 
14
16
  def get_members_to_add(repo, gl, app_sre):
15
- maintainers = gl.get_project_maintainers(repo)
17
+ maintainers = get_all_app_sre_maintainers(repo, gl, app_sre)
16
18
  if maintainers is None:
17
19
  return []
18
20
  if gl.user.username not in maintainers:
@@ -26,6 +28,20 @@ def get_members_to_add(repo, gl, app_sre):
26
28
  return members_to_add
27
29
 
28
30
 
31
+ def get_all_app_sre_maintainers(repo, gl, app_sre):
32
+ app_sre_user_ids = [user.id for user in app_sre]
33
+ chunks = batches.batched(app_sre_user_ids, PAGE_SIZE)
34
+ app_sre_maintainers = (
35
+ gl.get_project_maintainers(repo, query=create_user_ids_query(chunk))
36
+ for chunk in chunks
37
+ )
38
+ return list(itertools.chain.from_iterable(app_sre_maintainers))
39
+
40
+
41
+ def create_user_ids_query(ids):
42
+ return {"user_ids": ",".join(str(id) for id in ids)}
43
+
44
+
29
45
  @defer
30
46
  def run(dry_run, thread_pool_size=10, defer=None):
31
47
  instance = queries.get_gitlab_instance()
@@ -38,7 +54,6 @@ def run(dry_run, thread_pool_size=10, defer=None):
38
54
  results = threaded.run(
39
55
  get_members_to_add, repos, thread_pool_size, gl=gl, app_sre=app_sre
40
56
  )
41
-
42
57
  members_to_add = list(itertools.chain.from_iterable(results))
43
58
  for m in members_to_add:
44
59
  logging.info(["add_maintainer", m["repo"], m["user"].username])
@@ -1,10 +1,12 @@
1
1
  import logging
2
2
  import sys
3
- from datetime import datetime, timezone
3
+ from datetime import datetime, timedelta, timezone
4
4
 
5
+ from reconcile.slack_base import slackapi_from_queries
5
6
  from reconcile.statuspage.atlassian import AtlassianStatusPageProvider
6
7
  from reconcile.statuspage.integration import get_binding_state, get_status_pages
7
8
  from reconcile.statuspage.page import StatusMaintenance
9
+ from reconcile.statuspage.state import S3ComponentBindingState
8
10
  from reconcile.utils.differ import diff_iterables
9
11
  from reconcile.utils.runtime.integration import (
10
12
  NoParams,
@@ -44,6 +46,25 @@ class StatusPageMaintenancesIntegration(QontractReconcileIntegration[NoParams]):
44
46
  f"Delete StatusPage Maintenance is not supported at this time: {d.name}"
45
47
  )
46
48
 
49
+ def notify(
50
+ self,
51
+ dry_run: bool,
52
+ desired_state: list[StatusMaintenance],
53
+ binding_state: S3ComponentBindingState,
54
+ ) -> None:
55
+ now = datetime.now(timezone.utc)
56
+ slack = slackapi_from_queries(QONTRACT_INTEGRATION, init_usergroups=False)
57
+ for m in desired_state:
58
+ scheduled_start = datetime.fromisoformat(m.schedule_start)
59
+ if now <= scheduled_start <= now + timedelta(hours=1):
60
+ state_key = f"notifications/{m.name}"
61
+ if binding_state.state.exists(state_key):
62
+ continue
63
+ logging.info(f"Notify StatusPage Maintenance: {m.name}")
64
+ if not dry_run:
65
+ slack.chat_post_message(m.message)
66
+ binding_state.state.add(f"notifications/{m.name}")
67
+
47
68
  def run(self, dry_run: bool = False) -> None:
48
69
  binding_state = get_binding_state(self.name, self.secret_reader)
49
70
  pages = get_status_pages()
@@ -75,6 +96,11 @@ class StatusPageMaintenancesIntegration(QontractReconcileIntegration[NoParams]):
75
96
  current_state=current_state,
76
97
  provider=page_provider,
77
98
  )
99
+ self.notify(
100
+ dry_run=dry_run,
101
+ desired_state=desired_state,
102
+ binding_state=binding_state,
103
+ )
78
104
  except Exception:
79
105
  logging.exception(f"failed to reconcile statuspage {p.name}")
80
106
  error = True
@@ -0,0 +1,11 @@
1
+ from collections.abc import Generator, Iterable
2
+ from itertools import islice
3
+ from typing import Any
4
+
5
+
6
+ def batched(iterable: Iterable[Any], size: int) -> Generator:
7
+ if size < 1:
8
+ raise ValueError("n must be at least one")
9
+ it = iter(iterable)
10
+ while batch := tuple(islice(it, size)):
11
+ yield batch
@@ -223,14 +223,19 @@ class GitLabApi: # pylint: disable=too-many-public-methods
223
223
  return any(mr.title == title for mr in mrs)
224
224
 
225
225
  @retry()
226
- def get_project_maintainers(self, repo_url: str | None = None) -> list[str] | None:
226
+ def get_project_maintainers(
227
+ self, repo_url: str | None = None, query: dict | None = None
228
+ ) -> list[str] | None:
227
229
  if repo_url is None:
228
230
  project = self.project
229
231
  else:
230
232
  project = self.get_project(repo_url)
231
233
  if project is None:
232
234
  return None
233
- members = self.get_items(project.members.all)
235
+ if query:
236
+ members = self.get_items(project.members.all, query_parameters=query)
237
+ else:
238
+ members = self.get_items(project.members.all)
234
239
  return [m.username for m in members if m.access_level >= 40]
235
240
 
236
241
  def get_app_sre_group_users(self):