qontract-reconcile 0.10.1rc1143__py3-none-any.whl → 0.10.1rc1146__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.
- {qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/RECORD +9 -9
- reconcile/aws_version_sync/integration.py +2 -2
- reconcile/external_resources/manager.py +115 -62
- reconcile/external_resources/metrics.py +2 -0
- reconcile/external_resources/state.py +3 -1
- {qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc1146
|
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
|
{qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/RECORD
RENAMED
@@ -153,7 +153,7 @@ reconcile/aws_saml_idp/integration.py,sha256=ZaPW3JMH7bHhQQlxLSU7vlupYNZeuDkXFaI
|
|
153
153
|
reconcile/aws_saml_roles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
154
154
|
reconcile/aws_saml_roles/integration.py,sha256=ctPIZfHLhkmnUdIYdtchXuTgHE4FuNTMtKfnvXr6MY8,11264
|
155
155
|
reconcile/aws_version_sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
156
|
-
reconcile/aws_version_sync/integration.py,sha256=
|
156
|
+
reconcile/aws_version_sync/integration.py,sha256=x1nPU8l7tTvIZqIo8TIUWE0DqZMFpx-oxkNqfyR3ZV0,17287
|
157
157
|
reconcile/aws_version_sync/utils.py,sha256=x-45QT7zAwdNvCg7w_qJNwLaksFcfz1_6KQoD_0IVuA,1727
|
158
158
|
reconcile/aws_version_sync/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
159
159
|
reconcile/aws_version_sync/merge_request_manager/merge_request.py,sha256=2FbqLLdqxycWNvX1eNbwMjWSVBb7q0p-8t5Db0m7b4Q,4842
|
@@ -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=gBVO5dE8JyZ3xYcYik-MTIp_18oU7_hpYc_oztyfElQ,6753
|
198
198
|
reconcile/external_resources/integration_secrets_sync.py,sha256=dX09O3r6KURziUYYfiki10orNjOGVma-XojhVqd0ww4,1667
|
199
|
-
reconcile/external_resources/manager.py,sha256=
|
199
|
+
reconcile/external_resources/manager.py,sha256=SRrx44silyaKv_P8EWNW9JxpJADYh_CRwBmSE8DO6cs,17671
|
200
200
|
reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
|
201
|
-
reconcile/external_resources/metrics.py,sha256=
|
201
|
+
reconcile/external_resources/metrics.py,sha256=nMbyonGZEJDD1lYzpQY2eR9TNwvxYC4ZCcpi6wrExcM,1037
|
202
202
|
reconcile/external_resources/model.py,sha256=UuQgrnv-SSkvSEQQGeCE2IZkhXjLTCVkP_mw8zBZsIQ,8349
|
203
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=
|
205
|
+
reconcile/external_resources/state.py,sha256=UupSa6tl4-73_J6Fhisn-qHal3v3uAUS5s5sk85LGDs,9343
|
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
|
@@ -871,8 +871,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
|
|
871
871
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
872
872
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
873
873
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
874
|
-
qontract_reconcile-0.10.
|
875
|
-
qontract_reconcile-0.10.
|
876
|
-
qontract_reconcile-0.10.
|
877
|
-
qontract_reconcile-0.10.
|
878
|
-
qontract_reconcile-0.10.
|
874
|
+
qontract_reconcile-0.10.1rc1146.dist-info/METADATA,sha256=LRnpop_Uixs9kQ5whiHWPNORzAgYT4rMKIgvJat2_M8,2213
|
875
|
+
qontract_reconcile-0.10.1rc1146.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
876
|
+
qontract_reconcile-0.10.1rc1146.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
877
|
+
qontract_reconcile-0.10.1rc1146.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
878
|
+
qontract_reconcile-0.10.1rc1146.dist-info/RECORD,,
|
@@ -107,12 +107,12 @@ class ExternalResource(BaseModel):
|
|
107
107
|
) -> semver.VersionInfo:
|
108
108
|
if isinstance(v, semver.VersionInfo):
|
109
109
|
return v
|
110
|
-
return parse_semver(v, optional_minor_and_patch=True)
|
110
|
+
return parse_semver(str(v), optional_minor_and_patch=True)
|
111
111
|
|
112
112
|
@root_validator(pre=True)
|
113
113
|
def set_resource_engine_version_format(cls, values: dict) -> dict:
|
114
114
|
resource_engine_version, resource_engine_version_format = (
|
115
|
-
values.get("resource_engine_version"),
|
115
|
+
str(values.get("resource_engine_version")),
|
116
116
|
values.get("resource_engine_version_format"),
|
117
117
|
)
|
118
118
|
if not resource_engine_version:
|
@@ -3,6 +3,7 @@ from collections.abc import Iterable
|
|
3
3
|
from datetime import UTC, datetime
|
4
4
|
from enum import StrEnum
|
5
5
|
|
6
|
+
from pydantic import BaseModel
|
6
7
|
from sretoolbox.utils import threaded
|
7
8
|
|
8
9
|
from reconcile.external_resources.factories import (
|
@@ -88,6 +89,61 @@ class ReconcileAction(StrEnum):
|
|
88
89
|
DESTROY_ERROR = "Resource status in ERROR state"
|
89
90
|
|
90
91
|
|
92
|
+
class ReconciliationStatus(BaseModel):
|
93
|
+
reconcile_time: int = 0
|
94
|
+
resource_status: ResourceStatus
|
95
|
+
|
96
|
+
@property
|
97
|
+
def has_errors(self) -> bool:
|
98
|
+
return self.resource_status == ResourceStatus.ERROR
|
99
|
+
|
100
|
+
@property
|
101
|
+
def needs_secret_sync(self) -> bool:
|
102
|
+
return self.resource_status == ResourceStatus.PENDING_SECRET_SYNC
|
103
|
+
|
104
|
+
def publish_metrics(self, r: Reconciliation, spec: ExternalResourceSpec) -> None:
|
105
|
+
job_name = ReconciliationK8sJob(reconciliation=r).name()
|
106
|
+
|
107
|
+
metrics.set_gauge(
|
108
|
+
ExternalResourcesResourceStatus(
|
109
|
+
app=spec.namespace["app"]["name"],
|
110
|
+
environment=spec.namespace["environment"]["name"],
|
111
|
+
provision_provider=r.key.provision_provider,
|
112
|
+
provisioner_name=r.key.provisioner_name,
|
113
|
+
provider=r.key.provider,
|
114
|
+
identifier=r.key.identifier,
|
115
|
+
job_name=job_name,
|
116
|
+
status=self.resource_status,
|
117
|
+
),
|
118
|
+
1,
|
119
|
+
)
|
120
|
+
metrics.set_gauge(
|
121
|
+
ExternalResourcesReconcileTimeGauge(
|
122
|
+
app=spec.namespace["app"]["name"],
|
123
|
+
environment=spec.namespace["environment"]["name"],
|
124
|
+
provision_provider=r.key.provision_provider,
|
125
|
+
provisioner_name=r.key.provisioner_name,
|
126
|
+
provider=r.key.provider,
|
127
|
+
identifier=r.key.identifier,
|
128
|
+
job_name=job_name,
|
129
|
+
),
|
130
|
+
self.reconcile_time,
|
131
|
+
)
|
132
|
+
|
133
|
+
if self.has_errors:
|
134
|
+
metrics.inc_counter(
|
135
|
+
ExternalResourcesReconcileErrorsCounter(
|
136
|
+
app=spec.namespace["app"]["name"],
|
137
|
+
environment=spec.namespace["environment"]["name"],
|
138
|
+
provision_provider=r.key.provision_provider,
|
139
|
+
provisioner_name=r.key.provisioner_name,
|
140
|
+
provider=r.key.provider,
|
141
|
+
identifier=r.key.identifier,
|
142
|
+
job_name=job_name,
|
143
|
+
)
|
144
|
+
)
|
145
|
+
|
146
|
+
|
91
147
|
class ExternalResourcesManager:
|
92
148
|
def __init__(
|
93
149
|
self,
|
@@ -150,16 +206,17 @@ class ExternalResourcesManager:
|
|
150
206
|
state: ExternalResourceState,
|
151
207
|
) -> bool:
|
152
208
|
reconcile_action = self._get_reconcile_action(reconciliation, state)
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
209
|
+
if reconcile_action == ReconcileAction.NOOP:
|
210
|
+
return False
|
211
|
+
|
212
|
+
logging.info(
|
213
|
+
"Reconciling: Status: [%s], Action: [%s], reason: [%s], key:[%s]",
|
214
|
+
state.resource_status.value,
|
215
|
+
reconciliation.action.value,
|
216
|
+
reconcile_action.value,
|
217
|
+
reconciliation.key,
|
218
|
+
)
|
219
|
+
return True
|
163
220
|
|
164
221
|
def get_all_reconciliations(self) -> dict[str, set[Reconciliation]]:
|
165
222
|
"""Returns all reconciliations in a dict. Useful to return all data
|
@@ -212,24 +269,27 @@ class ExternalResourcesManager:
|
|
212
269
|
to_reconcile.add(r)
|
213
270
|
return to_reconcile
|
214
271
|
|
215
|
-
def
|
216
|
-
self,
|
217
|
-
|
272
|
+
def _get_reconciliation_status(
|
273
|
+
self,
|
274
|
+
r: Reconciliation,
|
275
|
+
state: ExternalResourceState,
|
276
|
+
) -> ReconciliationStatus:
|
218
277
|
"""Gets the resource reconciliation state from the Job and updates the
|
219
278
|
Resource state accordingly. It also returns if the target outputs secret needs
|
220
279
|
to be reconciled.
|
221
280
|
|
222
281
|
:param r: Reconciliation object
|
223
282
|
:param state: State object
|
224
|
-
:return:
|
283
|
+
:return: ReconciliationStatus
|
225
284
|
"""
|
226
|
-
|
285
|
+
|
286
|
+
status = ReconciliationStatus(resource_status=state.resource_status)
|
227
287
|
|
228
288
|
if state.resource_status not in {
|
229
289
|
ResourceStatus.DELETE_IN_PROGRESS,
|
230
290
|
ResourceStatus.IN_PROGRESS,
|
231
291
|
}:
|
232
|
-
return
|
292
|
+
return status
|
233
293
|
|
234
294
|
logging.info(
|
235
295
|
"Reconciliation In progress. Action: %s, Key:%s",
|
@@ -239,7 +299,8 @@ class ExternalResourcesManager:
|
|
239
299
|
|
240
300
|
# Need to check the reconciliation set in the state, not the desired one
|
241
301
|
# as the reconciliation object might be from a previous desired state
|
242
|
-
|
302
|
+
status.resource_status = state.resource_status
|
303
|
+
|
243
304
|
match self.reconciler.get_resource_reconcile_status(state.reconciliation):
|
244
305
|
case ReconcileStatus.SUCCESS:
|
245
306
|
logging.info(
|
@@ -248,22 +309,11 @@ class ExternalResourcesManager:
|
|
248
309
|
r.key,
|
249
310
|
)
|
250
311
|
if r.action == Action.APPLY:
|
251
|
-
|
252
|
-
self.state_mgr.set_external_resource_state(state)
|
253
|
-
need_secret_sync = True
|
312
|
+
status.resource_status = ResourceStatus.PENDING_SECRET_SYNC
|
254
313
|
elif r.action == Action.DESTROY:
|
255
|
-
|
256
|
-
|
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,
|
314
|
+
status.resource_status = ResourceStatus.DELETED
|
315
|
+
status.reconcile_time = (
|
316
|
+
self.reconciler.get_resource_reconcile_duration(r) or 0
|
267
317
|
)
|
268
318
|
case ReconcileStatus.ERROR:
|
269
319
|
logging.info(
|
@@ -271,29 +321,40 @@ class ExternalResourcesManager:
|
|
271
321
|
r.action.value,
|
272
322
|
r.key,
|
273
323
|
)
|
274
|
-
|
324
|
+
status.resource_status = ResourceStatus.ERROR
|
275
325
|
case ReconcileStatus.NOT_EXISTS:
|
276
326
|
logging.info(
|
277
327
|
"Reconciliation should exist but it doesn't. Marking as ERROR to retrigger: Action:%s, Key:%s",
|
278
328
|
r.action.value,
|
279
329
|
r.key,
|
280
330
|
)
|
281
|
-
|
282
|
-
|
283
|
-
|
331
|
+
status.resource_status = ResourceStatus.ERROR
|
332
|
+
|
333
|
+
return status
|
334
|
+
|
335
|
+
def _update_resource_state(
|
336
|
+
self,
|
337
|
+
r: Reconciliation,
|
338
|
+
state: ExternalResourceState,
|
339
|
+
status: ReconciliationStatus,
|
340
|
+
) -> None:
|
341
|
+
# There is no need to update the state
|
342
|
+
# if it's marked in progress
|
343
|
+
if state.resource_status in {
|
344
|
+
ResourceStatus.DELETE_IN_PROGRESS,
|
345
|
+
ResourceStatus.IN_PROGRESS,
|
346
|
+
}:
|
347
|
+
return
|
348
|
+
state.ts = datetime.now(UTC)
|
349
|
+
if status.resource_status == ResourceStatus.DELETED:
|
350
|
+
self.state_mgr.del_external_resource_state(r.key)
|
351
|
+
else:
|
352
|
+
state.resource_status = status.resource_status
|
284
353
|
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
|
-
)
|
294
|
-
return need_secret_sync
|
295
354
|
|
296
|
-
def
|
355
|
+
def _set_resource_reconciliation_in_progress(
|
356
|
+
self, r: Reconciliation, state: ExternalResourceState
|
357
|
+
) -> None:
|
297
358
|
state.ts = datetime.now(UTC)
|
298
359
|
if r.action == Action.APPLY:
|
299
360
|
state.resource_status = ResourceStatus.IN_PROGRESS
|
@@ -352,26 +413,18 @@ class ExternalResourcesManager:
|
|
352
413
|
to_sync_keys: set[ExternalResourceKey] = set()
|
353
414
|
for r in desired_r.union(deleted_r):
|
354
415
|
state = self.state_mgr.get_external_resource_state(r.key)
|
355
|
-
|
356
|
-
|
357
|
-
|
416
|
+
status = self._get_reconciliation_status(r, state)
|
417
|
+
self._update_resource_state(r, state, status)
|
418
|
+
|
419
|
+
if status.needs_secret_sync:
|
358
420
|
to_sync_keys.add(r.key)
|
359
421
|
|
360
422
|
if self._resource_needs_reconciliation(reconciliation=r, state=state):
|
361
423
|
self.reconciler.reconcile_resource(reconciliation=r)
|
362
|
-
self.
|
424
|
+
self._set_resource_reconciliation_in_progress(r, state)
|
363
425
|
|
364
|
-
|
365
|
-
|
366
|
-
provision_provider=r.key.provision_provider,
|
367
|
-
provisioner_name=r.key.provisioner_name,
|
368
|
-
provider=r.key.provider,
|
369
|
-
identifier=r.key.identifier,
|
370
|
-
job_name=job_name,
|
371
|
-
status=state.resource_status,
|
372
|
-
),
|
373
|
-
1,
|
374
|
-
)
|
426
|
+
if spec := self.er_inventory.get(r.key):
|
427
|
+
status.publish_metrics(r, spec)
|
375
428
|
|
376
429
|
pending_sync_keys = self.state_mgr.get_keys_by_status(
|
377
430
|
ResourceStatus.PENDING_SECRET_SYNC
|
@@ -74,7 +74,9 @@ class DynamoDBStateAdapter:
|
|
74
74
|
return item[key][_type]
|
75
75
|
|
76
76
|
def deserialize(
|
77
|
-
self,
|
77
|
+
self,
|
78
|
+
item: Mapping[str, Any],
|
79
|
+
partial_data: bool = False,
|
78
80
|
) -> ExternalResourceState:
|
79
81
|
_key = self._get_value(item, self.ER_KEY, _type="M")
|
80
82
|
key = ExternalResourceKey(
|
{qontract_reconcile-0.10.1rc1143.dist-info → qontract_reconcile-0.10.1rc1146.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|