qontract-reconcile 0.10.1rc1090__py3-none-any.whl → 0.10.1rc1092__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.1rc1090
3
+ Version: 0.10.1rc1092
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
@@ -196,13 +196,13 @@ reconcile/external_resources/aws.py,sha256=7W-6d-lXO6JGwaxtO1Uc3Lw0p8csJ1EVgz__O
196
196
  reconcile/external_resources/factories.py,sha256=nhdTqf1WEfRfgd5-70KAUJVz0ZvZ19C3Pz7wmotSdrs,4857
197
197
  reconcile/external_resources/integration.py,sha256=y1gJ16woMBC3J9qniMmS5y3lCkAs7V_ETZRUwjKqaO0,6628
198
198
  reconcile/external_resources/integration_secrets_sync.py,sha256=cMEZhgCvABAMf-DWF051L6CRnJQdfbsISA_b1xuS940,1670
199
- reconcile/external_resources/manager.py,sha256=FqOkwiWY6X8qlwE7yrwjiVxKKmZ3k0TdafQnqfihXF8,15107
199
+ reconcile/external_resources/manager.py,sha256=AvBYpC4p2BaGrxYEV1ut3V2FtJfs1h2z0Vk8Px6dCaQ,15641
200
200
  reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
201
- reconcile/external_resources/metrics.py,sha256=m2TIOao2N7pD6k45driFbBGVCC_N7ai44m-lLPfa5qk,454
201
+ reconcile/external_resources/metrics.py,sha256=Ihhe39BX0h2w7eLvvdijJuCXjj-f7w1E3MfDL0YiGNU,668
202
202
  reconcile/external_resources/model.py,sha256=UuQgrnv-SSkvSEQQGeCE2IZkhXjLTCVkP_mw8zBZsIQ,8349
203
- reconcile/external_resources/reconciler.py,sha256=E50X_lnOD0OWYXMzyZld1P6dCFJFYjHGyICWff9bxlc,9323
203
+ reconcile/external_resources/reconciler.py,sha256=3KFmkHsN7YAwJUSBpN1Xd_D2zM9Ea5_c2uMGWsfruZo,9707
204
204
  reconcile/external_resources/secrets_sync.py,sha256=6n0oDPLjd9Ql0lf6zsr1AZw8A6EEe3yCzl20XodtgkE,16229
205
- reconcile/external_resources/state.py,sha256=fKU6PLYOZ2ZTaIwvt1BNlNOnIqcewLijOyT3Lgcd1NE,9677
205
+ reconcile/external_resources/state.py,sha256=pjxqfjZ-DXmN_9dpfaAwhTtTDr4XrcPlwuQ7S3m25Eo,9326
206
206
  reconcile/glitchtip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
207
  reconcile/glitchtip/integration.py,sha256=XtewM9nfTPLnPSpYebP50GrveYOnhTvKNq3seSvL6u8,8343
208
208
  reconcile/glitchtip/reconciler.py,sha256=nUvDv7qG1ly0cA16MmlL6NV71yl1mJYLT2mui7lmi0Y,12402
@@ -767,7 +767,7 @@ reconcile/utils/jinja2/extensions.py,sha256=7K-uo6G2eCWa98MHT8fRPYIKCLQB_5D2keqQ
767
767
  reconcile/utils/jinja2/filters.py,sha256=JfO_14APySBPidsMvHXG-8dULNPddZCE15Umjk_aSBk,4830
768
768
  reconcile/utils/jinja2/utils.py,sha256=adxVFY4WvBGIToEEr8KwqLzp6uDxSipTBzZWwnRqbNQ,8700
769
769
  reconcile/utils/jobcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
770
- reconcile/utils/jobcontroller/controller.py,sha256=n9twSH_xZtp5vknM3qByp0eWCn9ED4U1a6yOFBS-GTg,14488
770
+ reconcile/utils/jobcontroller/controller.py,sha256=Vh08lZuCSIIceWGSDhBB00iFwTI9eeKZW1sfHlkAvSo,15373
771
771
  reconcile/utils/jobcontroller/models.py,sha256=x9YIvWfYOOvXNKToFVx1H7qDrZb0Sa1KI_4Y0gl7rMM,6336
772
772
  reconcile/utils/membershipsources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
773
773
  reconcile/utils/membershipsources/app_interface_resolver.py,sha256=MqDFvK3aXhmmMuMiIygC-onFVrrIopKHriaYJQ5jnuY,1988
@@ -870,8 +870,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
870
870
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
871
871
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
872
872
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
873
- qontract_reconcile-0.10.1rc1090.dist-info/METADATA,sha256=WP2Qi20ing6IiFnzyowq9CoR3Ds7qNU-ma6oiCJQspQ,2213
874
- qontract_reconcile-0.10.1rc1090.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
875
- qontract_reconcile-0.10.1rc1090.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
876
- qontract_reconcile-0.10.1rc1090.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
877
- qontract_reconcile-0.10.1rc1090.dist-info/RECORD,,
873
+ qontract_reconcile-0.10.1rc1092.dist-info/METADATA,sha256=aS-73Sg0ji6ejHZ2glug4akNnNufl-Qm-gdqoYfmFuI,2213
874
+ qontract_reconcile-0.10.1rc1092.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
875
+ qontract_reconcile-0.10.1rc1092.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
876
+ qontract_reconcile-0.10.1rc1092.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
877
+ qontract_reconcile-0.10.1rc1092.dist-info/RECORD,,
@@ -13,7 +13,10 @@ from reconcile.external_resources.factories import (
13
13
  TerraformModuleProvisionDataFactory,
14
14
  setup_aws_resource_factories,
15
15
  )
16
- from reconcile.external_resources.metrics import ExternalResourcesReconcileErrorsGauge
16
+ from reconcile.external_resources.metrics import (
17
+ ExternalResourcesReconcileErrorsCounter,
18
+ ExternalResourcesReconcileTimeGauge,
19
+ )
17
20
  from reconcile.external_resources.model import (
18
21
  Action,
19
22
  ExternalResource,
@@ -24,7 +27,10 @@ from reconcile.external_resources.model import (
24
27
  ModuleInventory,
25
28
  Reconciliation,
26
29
  )
27
- from reconcile.external_resources.reconciler import ExternalResourcesReconciler
30
+ from reconcile.external_resources.reconciler import (
31
+ ExternalResourcesReconciler,
32
+ ReconciliationK8sJob,
33
+ )
28
34
  from reconcile.external_resources.secrets_sync import InClusterSecretsReconciler
29
35
  from reconcile.external_resources.state import (
30
36
  ExternalResourcesStateDynamoDB,
@@ -232,6 +238,7 @@ class ExternalResourcesManager:
232
238
 
233
239
  # Need to check the reconciliation set in the state, not the desired one
234
240
  # as the reconciliation object might be from a previous desired state
241
+ job_name = ReconciliationK8sJob(reconciliation=r).name()
235
242
  error = False
236
243
  match self.reconciler.get_resource_reconcile_status(state.reconciliation):
237
244
  case ReconcileStatus.SUCCESS:
@@ -242,12 +249,22 @@ class ExternalResourcesManager:
242
249
  )
243
250
  if r.action == Action.APPLY:
244
251
  state.resource_status = ResourceStatus.PENDING_SECRET_SYNC
245
- state.reconciliation_errors = 0
246
252
  self.state_mgr.set_external_resource_state(state)
247
253
  need_secret_sync = True
248
254
  elif r.action == Action.DESTROY:
249
255
  state.resource_status = ResourceStatus.DELETED
250
256
  self.state_mgr.del_external_resource_state(r.key)
257
+
258
+ metrics.set_gauge(
259
+ ExternalResourcesReconcileTimeGauge(
260
+ provision_provider=r.key.provision_provider,
261
+ provisioner_name=r.key.provisioner_name,
262
+ provider=r.key.provider,
263
+ identifier=r.key.identifier,
264
+ job_name=job_name,
265
+ ),
266
+ self.reconciler.get_resource_reconcile_duration(r) or 0,
267
+ )
251
268
  case ReconcileStatus.ERROR:
252
269
  logging.info(
253
270
  "Reconciliation ended with ERROR: Action:%s, Key:%s",
@@ -264,8 +281,16 @@ class ExternalResourcesManager:
264
281
  error = True
265
282
  if error:
266
283
  state.resource_status = ResourceStatus.ERROR
267
- state.reconciliation_errors += 1
268
284
  self.state_mgr.set_external_resource_state(state)
285
+ metrics.inc_counter(
286
+ ExternalResourcesReconcileErrorsCounter(
287
+ provision_provider=r.key.provision_provider,
288
+ provisioner_name=r.key.provisioner_name,
289
+ provider=r.key.provider,
290
+ identifier=r.key.identifier,
291
+ job_name=job_name,
292
+ )
293
+ )
269
294
 
270
295
  return need_secret_sync
271
296
 
@@ -332,16 +357,6 @@ class ExternalResourcesManager:
332
357
  if need_sync:
333
358
  to_sync_keys.add(r.key)
334
359
 
335
- metrics.set_gauge(
336
- ExternalResourcesReconcileErrorsGauge(
337
- provision_provider=r.key.provision_provider,
338
- provisioner_name=r.key.provisioner_name,
339
- provider=r.key.provider,
340
- identifier=r.key.identifier,
341
- ),
342
- float(state.reconciliation_errors),
343
- )
344
-
345
360
  if self._resource_needs_reconciliation(reconciliation=r, state=state):
346
361
  self.reconciler.reconcile_resource(reconciliation=r)
347
362
  self._update_state(r, state)
@@ -1,20 +1,26 @@
1
1
  from pydantic import BaseModel
2
2
 
3
- from reconcile.utils.metrics import (
4
- GaugeMetric,
5
- )
3
+ from reconcile.utils.metrics import CounterMetric, GaugeMetric
6
4
 
7
5
 
8
6
  class ExternalResourcesBaseMetric(BaseModel):
9
7
  integration = "external_resources"
10
-
11
-
12
- class ExternalResourcesReconcileErrorsGauge(ExternalResourcesBaseMetric, GaugeMetric):
13
8
  provision_provider: str
14
9
  provisioner_name: str
15
10
  provider: str
16
11
  identifier: str
12
+ job_name: str
13
+
14
+
15
+ class ExternalResourcesReconcileErrorsCounter(
16
+ ExternalResourcesBaseMetric, CounterMetric
17
+ ):
18
+ @classmethod
19
+ def name(cls) -> str:
20
+ return "external_resources_reconcile_errors"
21
+
17
22
 
23
+ class ExternalResourcesReconcileTimeGauge(ExternalResourcesBaseMetric, GaugeMetric):
18
24
  @classmethod
19
25
  def name(cls) -> str:
20
- return "external_resources_reconcile_status"
26
+ return "external_resources_reconcile_time"
@@ -38,6 +38,11 @@ class ExternalResourcesReconciler(ABC):
38
38
  reconciliation: Reconciliation,
39
39
  ) -> ReconcileStatus: ...
40
40
 
41
+ @abstractmethod
42
+ def get_resource_reconcile_duration(
43
+ self, reconciliation: Reconciliation
44
+ ) -> int | None: ...
45
+
41
46
  @abstractmethod
42
47
  def reconcile_resource(self, reconciliation: Reconciliation) -> None: ...
43
48
 
@@ -199,6 +204,12 @@ class K8sExternalResourcesReconciler(ExternalResourcesReconciler):
199
204
  job_name = ReconciliationK8sJob(reconciliation=reconciliation).name()
200
205
  return ReconcileStatus(self.controller.get_job_status(job_name))
201
206
 
207
+ def get_resource_reconcile_duration(
208
+ self, reconciliation: Reconciliation
209
+ ) -> int | None:
210
+ job_name = ReconciliationK8sJob(reconciliation=reconciliation).name()
211
+ return self.controller.get_success_job_duration(job_name)
212
+
202
213
  def reconcile_resource(self, reconciliation: Reconciliation) -> None:
203
214
  concurrency_policy = (
204
215
  JobConcurrencyPolicy.REPLACE_FAILED | JobConcurrencyPolicy.REPLACE_FINISHED
@@ -44,7 +44,6 @@ class ExternalResourceState(BaseModel):
44
44
  ts: datetime
45
45
  resource_status: ResourceStatus
46
46
  reconciliation: Reconciliation
47
- reconciliation_errors: int = 0
48
47
 
49
48
 
50
49
  class DynamoDBStateAdapter:
@@ -53,7 +52,6 @@ class DynamoDBStateAdapter:
53
52
 
54
53
  RESOURCE_STATUS = "resource_status"
55
54
  TIMESTAMP = "time_stamp"
56
- RECONCILIATION_ERRORS = "reconciliation_errors"
57
55
 
58
56
  ER_KEY = "external_resource_key"
59
57
  ER_KEY_PROVISION_PROVIDER = "provision_provider"
@@ -120,9 +118,6 @@ class DynamoDBStateAdapter:
120
118
  ts=self._get_value(item, self.TIMESTAMP),
121
119
  resource_status=self._get_value(item, self.RESOURCE_STATUS),
122
120
  reconciliation=r,
123
- reconciliation_errors=int(
124
- self._get_value(item, self.RECONCILIATION_ERRORS, _type="N")
125
- ),
126
121
  )
127
122
 
128
123
  def serialize(self, state: ExternalResourceState) -> dict[str, Any]:
@@ -130,7 +125,6 @@ class DynamoDBStateAdapter:
130
125
  self.ER_KEY_HASH: {"S": state.key.hash()},
131
126
  self.TIMESTAMP: {"S": state.ts.isoformat()},
132
127
  self.RESOURCE_STATUS: {"S": state.resource_status.value},
133
- self.RECONCILIATION_ERRORS: {"N": str(state.reconciliation_errors)},
134
128
  self.ER_KEY: {
135
129
  "M": {
136
130
  self.ER_KEY_PROVISION_PROVIDER: {"S": state.key.provision_provider},
@@ -176,7 +170,6 @@ class ExternalResourcesStateDynamoDB:
176
170
  DynamoDBStateAdapter.ER_KEY,
177
171
  DynamoDBStateAdapter.TIMESTAMP,
178
172
  DynamoDBStateAdapter.RESOURCE_STATUS,
179
- DynamoDBStateAdapter.RECONCILIATION_ERRORS,
180
173
  f"{DynamoDBStateAdapter.RECONC}.{DynamoDBStateAdapter.RECONC_RESOURCE_HASH}",
181
174
  ])
182
175
 
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import time
3
+ from datetime import datetime
3
4
  from typing import Protocol, TextIO
4
5
 
5
6
  from kubernetes.client import ( # type: ignore[attr-defined]
@@ -143,6 +144,25 @@ class K8sJobController:
143
144
  return JobStatus.ERROR
144
145
  return JobStatus.IN_PROGRESS
145
146
 
147
+ def get_success_job_duration(self, job_name: str) -> int | None:
148
+ """
149
+ Returns the number of seconds the job took to complete.
150
+ * If a job is not completed with success, returns None
151
+ """
152
+ if self.get_job_status(job_name) != JobStatus.SUCCESS:
153
+ return None
154
+ job_resource = self.cache.get(job_name)
155
+ if job_resource is None:
156
+ return None
157
+ status = job_resource.body.get("status") or {}
158
+ start_time = status.get("startTime", None)
159
+ completion_time = status.get("completionTime", None)
160
+ if not completion_time or not start_time:
161
+ return None
162
+ dt_start_time = datetime.fromisoformat(start_time)
163
+ dt_completion_time = datetime.fromisoformat(completion_time)
164
+ return int((dt_completion_time - dt_start_time).total_seconds())
165
+
146
166
  def wait_for_job_list_completion(
147
167
  self, job_names: set[str], check_interval_seconds: int, timeout_seconds: int
148
168
  ) -> dict[str, JobStatus]: