qontract-reconcile 0.10.2.dev180__py3-none-any.whl → 0.10.2.dev181__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.dev180
3
+ Version: 0.10.2.dev181
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
@@ -482,11 +482,11 @@ reconcile/rhidp/sso_client/base.py,sha256=EfQ2ewcOKh5idg46UKAkY6z0m_nGQfvnQKffa2
482
482
  reconcile/rhidp/sso_client/integration.py,sha256=kA8g7c38ZBSdrRtyfEqy_WgSreD1PbwY7ZIN-3tZRPc,2221
483
483
  reconcile/rhidp/sso_client/metrics.py,sha256=Tq7tSOsqL3XdcPUdozxqzSPIodUeOV87UCTqpuuqqhw,1013
484
484
  reconcile/saas_auto_promotions_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
485
- reconcile/saas_auto_promotions_manager/integration.py,sha256=haXTpwi0rEQtN9-MadBzFvSNtbxw5QQuFpMQLg3bZlQ,6707
485
+ reconcile/saas_auto_promotions_manager/integration.py,sha256=PhngUNVQMdZ_7KlIOCokdYqQ2k-XdsvvfCb8BAdSiAA,6889
486
486
  reconcile/saas_auto_promotions_manager/meta.py,sha256=76Jp50r6Y_KyJoXFfSjrt5YrCtXyg_A4FXXxHYiS3TE,161
487
487
  reconcile/saas_auto_promotions_manager/publisher.py,sha256=5gphMxr2NUvyB7WDK4eAbgZeyeF30cZ3a2ZGrbFQgZk,2976
488
488
  reconcile/saas_auto_promotions_manager/s3_exporter.py,sha256=Y-r5R6viiAzglUHbYKItYSjT_axmLlPEJVmu_H6N170,2682
489
- reconcile/saas_auto_promotions_manager/subscriber.py,sha256=tZLcjPt64cIxiswlSGpEcAg3VTp0PHvnDqgFQ2pajVM,10044
489
+ reconcile/saas_auto_promotions_manager/subscriber.py,sha256=V8e2tz0s3A-pcUNb2UoREQm6-6ZzS4F3gJdGUBoa9WY,11281
490
490
  reconcile/saas_auto_promotions_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
491
491
  reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py,sha256=R2CRtjdOggY5lSqt-A5qz2ymqomx4opVeVV_oqBAW0A,7804
492
492
  reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py,sha256=isY8frVsL3PlcdZmdZ4O0qyp76oczl4DUMX9uMArs5Y,1222
@@ -497,7 +497,7 @@ reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py,sha256
497
497
  reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py,sha256=-qGQOh6Jdp4lomNDij3zWVC0pl6uPHFWS5Woqcp5HQk,410
498
498
  reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=EgImn9SpeThTNN5-P5lvz__bMKZNU4m2SfKpocRCy5w,7278
499
499
  reconcile/saas_auto_promotions_manager/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
500
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=LhK3OtHf-l_wm1SZCb3vZUrPpNZJRpM65WKY2HATXRY,8606
500
+ reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=BPzSsuXy5ULKSo36v1bItC3rU-AEFfVZeWXZw1tXqLg,9674
501
501
  reconcile/skupper_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
502
502
  reconcile/skupper_network/integration.py,sha256=oZIVDBRcQPC-lWxNFiJhGbtCM7Yj7fjwAzYZ8JvVe3I,10789
503
503
  reconcile/skupper_network/models.py,sha256=HEwlVKsbmMaKaaBGvITIiSYNEVdjwXVhLaOJgLSZ2xQ,6604
@@ -569,7 +569,7 @@ reconcile/typed_queries/pagerduty_instances.py,sha256=zxCNxMak4iikryePaRi71lTADV
569
569
  reconcile/typed_queries/quay.py,sha256=3IMy9jjHF2f9t47EXZOQVA3p0nFkWFhaFhxhvib-71o,644
570
570
  reconcile/typed_queries/repos.py,sha256=8A93dKDt6igT4ClqMjt7YUTsoP4qh1Wnm0W3xsMgj48,824
571
571
  reconcile/typed_queries/reserved_networks.py,sha256=XY9y3amtIQT0n06O0Toubqr_UmylJ2ELAv9-BJCK890,345
572
- reconcile/typed_queries/saas_files.py,sha256=O2kd0nSFfMgnbXvSv9oMIdlBGZg7XlOU3y2CWg1W2DQ,14001
572
+ reconcile/typed_queries/saas_files.py,sha256=SOE36sWPBcuaRmEaNxXCQZMQdJiUZX8_A92o42XwHQA,14141
573
573
  reconcile/typed_queries/slack.py,sha256=r30lspctHloyygPn8_DVybxPwUWwiBpvBRRXiTVcQYk,251
574
574
  reconcile/typed_queries/slo_documents.py,sha256=YMdox_-lBRqrdxamPhdnUlRTY_Ro35ptsupq7OaynUQ,362
575
575
  reconcile/typed_queries/smtp.py,sha256=aSLglYa5bHKmlGwKkxq2RZqyMWuAf0a4S_mOuhDa084,542
@@ -807,7 +807,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
807
807
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
808
808
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
809
809
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
810
- qontract_reconcile-0.10.2.dev180.dist-info/METADATA,sha256=3ABcj9gi3YuKUyzROkTB3i4Mo6F6eXVi4BpgPXIwLg4,24627
811
- qontract_reconcile-0.10.2.dev180.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
- qontract_reconcile-0.10.2.dev180.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
- qontract_reconcile-0.10.2.dev180.dist-info/RECORD,,
810
+ qontract_reconcile-0.10.2.dev181.dist-info/METADATA,sha256=GFcwt6xmX1m6ffKhsTJe6HD5YUe_VT-8-OiGbWIN2cE,24627
811
+ qontract_reconcile-0.10.2.dev181.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
+ qontract_reconcile-0.10.2.dev181.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
+ qontract_reconcile-0.10.2.dev181.dist-info/RECORD,,
@@ -94,6 +94,7 @@ class SaasAutoPromotionsManager:
94
94
 
95
95
  def init_external_dependencies(
96
96
  dry_run: bool,
97
+ thread_pool_size: int,
97
98
  env_name: str | None = None,
98
99
  app_name: str | None = None,
99
100
  ) -> tuple[
@@ -139,7 +140,11 @@ def init_external_dependencies(
139
140
  renderer=Renderer(),
140
141
  )
141
142
  saas_files = get_saas_files(env_name=env_name, app_name=app_name)
142
- saas_inventory = SaasFilesInventory(saas_files=saas_files)
143
+ saas_inventory = SaasFilesInventory(
144
+ saas_files=saas_files,
145
+ secret_reader=secret_reader,
146
+ thread_pool_size=thread_pool_size,
147
+ )
143
148
  saas_deploy_state = init_state(
144
149
  integration=OPENSHIFT_SAAS_DEPLOY, secret_reader=secret_reader
145
150
  )
@@ -181,7 +186,10 @@ def run(
181
186
  saas_deploy_state,
182
187
  sapm_state,
183
188
  ) = init_external_dependencies(
184
- dry_run=dry_run, env_name=env_name, app_name=app_name
189
+ dry_run=dry_run,
190
+ env_name=env_name,
191
+ app_name=app_name,
192
+ thread_pool_size=thread_pool_size,
185
193
  )
186
194
  if defer:
187
195
  defer(vcs.cleanup)
@@ -13,6 +13,7 @@ from reconcile.saas_auto_promotions_manager.publisher import (
13
13
  DeploymentInfo,
14
14
  Publisher,
15
15
  )
16
+ from reconcile.utils.slo_document_manager import SLODocumentManager
16
17
 
17
18
  CONTENT_HASH_LENGTH = 32
18
19
 
@@ -47,7 +48,9 @@ class Subscriber:
47
48
  uid: str,
48
49
  soak_days: int,
49
50
  blocked_versions: set[str],
51
+ hotfix_versions: set[str],
50
52
  schedule: str,
53
+ slo_document_manager: SLODocumentManager | None = None,
51
54
  ):
52
55
  self.saas_name = saas_name
53
56
  self.template_name = template_name
@@ -64,6 +67,8 @@ class Subscriber:
64
67
  self._content_hash = ""
65
68
  self._use_target_config_hash = use_target_config_hash
66
69
  self._blocked_versions = blocked_versions
70
+ self._hotfix_versions = hotfix_versions
71
+ self.slo_document_manager = slo_document_manager
67
72
 
68
73
  def has_diff(self) -> bool:
69
74
  current_hashes = {
@@ -192,6 +197,10 @@ class Subscriber:
192
197
  return
193
198
 
194
199
  desired_ref = next(iter(publisher_refs))
200
+ # validate slo gatekeeping
201
+ if self._has_breached_slos(desired_ref=desired_ref):
202
+ return
203
+
195
204
  if desired_ref in self._blocked_versions:
196
205
  logging.info(
197
206
  "Subscriber at path %s promotion stopped because of blocked ref: %s",
@@ -203,6 +212,25 @@ class Subscriber:
203
212
  # Passed all gates -> lets promote desired ref
204
213
  self.desired_ref = desired_ref
205
214
 
215
+ def _has_breached_slos(self, desired_ref: str) -> bool:
216
+ if self.slo_document_manager and desired_ref not in self._hotfix_versions:
217
+ breached_slos = self.slo_document_manager.get_breached_slos()
218
+ if breached_slos:
219
+ logging.info(
220
+ "Subscriber at path %s promotion stopped because following breached SLOs",
221
+ self.target_file_path,
222
+ )
223
+ for slo in breached_slos:
224
+ logging.info(
225
+ "SLO:%s of document %s is breached. Current value:%f Expected:%f ",
226
+ slo.slo.name,
227
+ slo.slo_document_name,
228
+ slo.current_slo_value,
229
+ slo.slo.slo_target,
230
+ )
231
+ return True
232
+ return False
233
+
206
234
  def _compute_desired_config_hashes(self) -> None:
207
235
  """
208
236
  Compute the desired config hashes for this subscriber.
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections import defaultdict
2
3
  from collections.abc import Iterable
3
4
 
4
5
  from reconcile.gql_definitions.common.saas_files import ParentSaasPromotionV1
@@ -8,7 +9,9 @@ from reconcile.saas_auto_promotions_manager.subscriber import (
8
9
  ConfigHash,
9
10
  Subscriber,
10
11
  )
11
- from reconcile.typed_queries.saas_files import SaasFile
12
+ from reconcile.typed_queries.saas_files import SaasFile, SaasResourceTemplateTarget
13
+ from reconcile.utils.secret_reader import SecretReaderBase
14
+ from reconcile.utils.slo_document_manager import SLODocumentManager
12
15
 
13
16
 
14
17
  class SaasFileInventoryError(Exception):
@@ -25,8 +28,15 @@ class SaasFilesInventory:
25
28
  This basically spans a directed graph, with subscribers as the root.
26
29
  """
27
30
 
28
- def __init__(self, saas_files: Iterable[SaasFile]):
31
+ def __init__(
32
+ self,
33
+ saas_files: Iterable[SaasFile],
34
+ secret_reader: SecretReaderBase,
35
+ thread_pool_size: int,
36
+ ):
29
37
  self._saas_files = saas_files
38
+ self.secret_reader = secret_reader
39
+ self.thread_pool_size = thread_pool_size
30
40
  self._channels_by_name: dict[str, Channel] = {}
31
41
  self.subscribers: list[Subscriber] = []
32
42
  self.publishers: list[Publisher] = []
@@ -86,10 +96,13 @@ class SaasFilesInventory:
86
96
 
87
97
  def _assemble_subscribers_with_auto_promotions(self) -> None:
88
98
  for saas_file in self._saas_files:
89
- blocked_versions: dict[str, set[str]] = {}
99
+ blocked_versions: dict[str, set[str]] = defaultdict(set[str])
100
+ hotfix_versions: dict[str, set[str]] = defaultdict(set[str])
90
101
  for code_component in saas_file.app.code_components or []:
91
102
  for version in code_component.blocked_versions or []:
92
- blocked_versions.setdefault(code_component.url, set()).add(version)
103
+ blocked_versions[code_component.url].add(version)
104
+ for hf_version in code_component.hotfix_versions or []:
105
+ hotfix_versions[code_component.url].add(hf_version)
93
106
  for resource_template in saas_file.resource_templates:
94
107
  for target in resource_template.targets:
95
108
  file_path = target.path or saas_file.path
@@ -101,6 +114,7 @@ class SaasFilesInventory:
101
114
  continue
102
115
  soak_days = target.promotion.soak_days or 0
103
116
  schedule = target.promotion.schedule or "* * * * *"
117
+
104
118
  subscriber = Subscriber(
105
119
  uid=target.uid(
106
120
  parent_saas_file_name=saas_file.name,
@@ -112,10 +126,10 @@ class SaasFilesInventory:
112
126
  ref=target.ref,
113
127
  target_namespace=target.namespace,
114
128
  soak_days=soak_days,
129
+ slo_document_manager=self._build_slo_document_manager(target),
115
130
  schedule=schedule,
116
- blocked_versions=blocked_versions.get(
117
- resource_template.url, set()
118
- ),
131
+ hotfix_versions=hotfix_versions[resource_template.url],
132
+ blocked_versions=blocked_versions[resource_template.url],
119
133
  use_target_config_hash=bool(
120
134
  target.promotion.redeploy_on_publisher_config_change
121
135
  ),
@@ -158,6 +172,17 @@ class SaasFilesInventory:
158
172
  self._channels_by_name[subscribe_channel]
159
173
  )
160
174
 
175
+ def _build_slo_document_manager(
176
+ self, target: SaasResourceTemplateTarget
177
+ ) -> SLODocumentManager | None:
178
+ if target.slos:
179
+ return SLODocumentManager(
180
+ slo_documents=target.slos,
181
+ secret_reader=self.secret_reader,
182
+ thread_pool_size=self.thread_pool_size,
183
+ )
184
+ return None
185
+
161
186
  def _remove_unsupported(self) -> None:
162
187
  """
163
188
  Lets remove subscribers from which we know we do not support them and log an error.
@@ -39,6 +39,7 @@ from reconcile.gql_definitions.common.saasherder_settings import AppInterfaceSet
39
39
  from reconcile.gql_definitions.common.saasherder_settings import (
40
40
  query as saasherder_settings_query,
41
41
  )
42
+ from reconcile.gql_definitions.fragments.saas_slo_document import SLODocument
42
43
  from reconcile.gql_definitions.fragments.saas_target_namespace import (
43
44
  SaasTargetNamespace,
44
45
  )
@@ -63,6 +64,7 @@ class SaasResourceTemplateTarget(ConfiguredBaseModel):
63
64
  secret_parameters: (
64
65
  list[SaasResourceTemplateTargetV2_SaasSecretParametersV1] | None
65
66
  ) = Field(..., alias="secretParameters")
67
+ slos: list[SLODocument] | None = Field(..., alias="slos")
66
68
  upstream: SaasResourceTemplateTargetUpstreamV1 | None = Field(..., alias="upstream")
67
69
  images: list[SaasResourceTemplateTargetImageV1] | None = Field(..., alias="images")
68
70
  disable: bool | None = Field(..., alias="disable")