qontract-reconcile 0.10.1rc1024__py3-none-any.whl → 0.10.1rc1026__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.1rc1024
3
+ Version: 0.10.1rc1026
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
@@ -181,7 +181,7 @@ reconcile/cna/assets/asset_factory.py,sha256=7T7X_J6xIsoGETqBRI45_EyIKEdQcnRPt_G
181
181
  reconcile/cna/assets/null.py,sha256=85mVh97atCoC0aLuX47poTZiyOthmziJeBsUw0c924w,1658
182
182
  reconcile/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
183
183
  reconcile/dynatrace_token_provider/dependencies.py,sha256=41q05A4C_eS3E8-MR4veeMxtQNsPoGdxmEa3d-OKxq4,2814
184
- reconcile/dynatrace_token_provider/integration.py,sha256=QY-k5vsbBOm80yW_RB6G2JZD5NY21zCyqHigos7GjRM,20876
184
+ reconcile/dynatrace_token_provider/integration.py,sha256=jOpj2qtkI95b_Ih0IH3cYV5G63RBuxhG6kX4RADBWeg,23003
185
185
  reconcile/dynatrace_token_provider/metrics.py,sha256=xiKkl8fTEBQaXJelGCPNTZhHAWdO1M3pCXNr_Tei63c,1285
186
186
  reconcile/dynatrace_token_provider/model.py,sha256=gkpqo5rRRueBXnIMjp4EEHqBUBuU65TRI8zpdb8GJ0A,241
187
187
  reconcile/dynatrace_token_provider/ocm.py,sha256=iHMsgbsLs-dlrB9UXmWNDF7E4UDe49JOsLa9rnowKfo,4282
@@ -194,10 +194,10 @@ reconcile/external_resources/aws.py,sha256=JvjKaABy2Pg8u8Lq82Acv4zMvpE3_qGKes7OG
194
194
  reconcile/external_resources/factories.py,sha256=DXgaLxoO87zZ76VOpRpu2GeYGhsbfOnOx5mrzgo4Gf4,4767
195
195
  reconcile/external_resources/integration.py,sha256=y1gJ16woMBC3J9qniMmS5y3lCkAs7V_ETZRUwjKqaO0,6628
196
196
  reconcile/external_resources/integration_secrets_sync.py,sha256=cMEZhgCvABAMf-DWF051L6CRnJQdfbsISA_b1xuS940,1670
197
- reconcile/external_resources/manager.py,sha256=xVwcFAPFG0HyMl_uzj8D6XXdH-Zmcw541DX0IHs57Jc,15278
198
- reconcile/external_resources/meta.py,sha256=cMT9OsKcUY26qwEjlQ02EkorvOBNqWj0JVMwfJa3Mg0,634
197
+ reconcile/external_resources/manager.py,sha256=Q1l53Q0QHS7dvxCU66_BBpWLpNf6he0FfNzxVKJmVN8,15102
198
+ reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
199
199
  reconcile/external_resources/metrics.py,sha256=m2TIOao2N7pD6k45driFbBGVCC_N7ai44m-lLPfa5qk,454
200
- reconcile/external_resources/model.py,sha256=oXxJkjhV53lwwAuxUCBrjJ8aCJmQdgcKWv68ugJPK4k,7229
200
+ reconcile/external_resources/model.py,sha256=RjGMHgt_J1_HQjfBodFGvdWpYKJupKB4xtWuggSnMtA,8270
201
201
  reconcile/external_resources/reconciler.py,sha256=E50X_lnOD0OWYXMzyZld1P6dCFJFYjHGyICWff9bxlc,9323
202
202
  reconcile/external_resources/secrets_sync.py,sha256=6n0oDPLjd9Ql0lf6zsr1AZw8A6EEe3yCzl20XodtgkE,16229
203
203
  reconcile/external_resources/state.py,sha256=fKU6PLYOZ2ZTaIwvt1BNlNOnIqcewLijOyT3Lgcd1NE,9677
@@ -288,8 +288,8 @@ reconcile/gql_definitions/endpoints_discovery/__init__.py,sha256=47DEQpj8HBSa-_T
288
288
  reconcile/gql_definitions/endpoints_discovery/namespaces.py,sha256=FqJ0H7NdsIm5BgVnuJV9wLcj7i667VhCN559tWJ-WsA,3054
289
289
  reconcile/gql_definitions/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
290
290
  reconcile/gql_definitions/external_resources/aws_accounts.py,sha256=XR69j9dpTQ0gv8y-AZN7AJ0dPvO-wbHscyCDgrax6Bk,2046
291
- reconcile/gql_definitions/external_resources/external_resources_modules.py,sha256=g2KB2wRnb8zF7xCmDJJFmiRdE4z4aYa9HtY3vCBVwMA,2441
292
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py,sha256=pXx58C8a2TfClsPqdRmRlQ3ILAX7Irnz-Ank8C8N_gM,40994
291
+ reconcile/gql_definitions/external_resources/external_resources_modules.py,sha256=HFOQjmNbNxk0j5nChxppQeCnJjeDsqibJkPgA7R1zRw,2417
292
+ reconcile/gql_definitions/external_resources/external_resources_namespaces.py,sha256=uNk0HOAH96qdLuDBE6VzjcIPB--PHvOLDitJ2IzPM40,41618
293
293
  reconcile/gql_definitions/external_resources/external_resources_settings.py,sha256=Hw9n_90BPG6Lnt2PT3mHc6p0KEm2CxKxvSGRFc_Dhus,2982
294
294
  reconcile/gql_definitions/fragments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
295
295
  reconcile/gql_definitions/fragments/aus_organization.py,sha256=uBKbTuBa3CZmTXR5HOcGhRcu2U9kM93KbYmoWTxcpB0,4767
@@ -668,7 +668,7 @@ reconcile/utils/environ.py,sha256=psk07d2xyjbUzjOCDdNWgavaNolL_t2sq3sn2gFfY9k,50
668
668
  reconcile/utils/exceptions.py,sha256=DwfnWUpVOotpP79RWZ2pycmG6nKCL00RBIeZLYkQPW4,635
669
669
  reconcile/utils/expiration.py,sha256=3JaXH4psksR7z262k7FmdyREjCLqm66OpVMEbcfdWRo,1213
670
670
  reconcile/utils/extended_early_exit.py,sha256=QSktrmfw37zSRMNk930tDbQsVeKxaPPPD43e79DGwZw,6754
671
- reconcile/utils/external_resource_spec.py,sha256=3z3Y2JGY8cO7hPc8JHkdczmrP81D7TKQ4uT128WhE_M,7006
671
+ reconcile/utils/external_resource_spec.py,sha256=bhH_xneFwATdFumTPkiQmcVKYI0gcaWuqV6FpFdf_P0,7006
672
672
  reconcile/utils/external_resources.py,sha256=ObBOGBRTsDQ2s9rojfeUfFMoR8ls4Kg-AnpsyF6m7u8,7539
673
673
  reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,402
674
674
  reconcile/utils/git.py,sha256=JkpbUO10oBTtNHZ1IhjyG6dTOUizc7I5H0vm7NvDVNw,1409
@@ -834,7 +834,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
834
834
  tools/app_interface_reporter.py,sha256=1ZP58LYV6ww3XOLVxgy8NKasMb1jQmp4BNqzTEB0VBE,17723
835
835
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
836
836
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
837
- tools/qontract_cli.py,sha256=bFTYOymVksNKp_ntG2l9GoA40tODuxeRDU3rO4J-CjA,128875
837
+ tools/qontract_cli.py,sha256=8HbIKtepnmoQteb9Q31iJXFG7_y2o_g6JEkIhXnoncM,128916
838
838
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
839
839
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
840
840
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -865,8 +865,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
865
865
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
866
866
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
867
867
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
868
- qontract_reconcile-0.10.1rc1024.dist-info/METADATA,sha256=mM2v2AIw7KVQIg5kmx124zqndS8w68EyqTisqYNRSs0,2213
869
- qontract_reconcile-0.10.1rc1024.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
870
- qontract_reconcile-0.10.1rc1024.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
871
- qontract_reconcile-0.10.1rc1024.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
872
- qontract_reconcile-0.10.1rc1024.dist-info/RECORD,,
868
+ qontract_reconcile-0.10.1rc1026.dist-info/METADATA,sha256=h_rqqk8rQyYaSa2vo9DevQxSauMk3iFNdbW8186aen0,2213
869
+ qontract_reconcile-0.10.1rc1026.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
870
+ qontract_reconcile-0.10.1rc1026.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
871
+ qontract_reconcile-0.10.1rc1026.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
872
+ qontract_reconcile-0.10.1rc1026.dist-info/RECORD,,
@@ -1,6 +1,7 @@
1
1
  import base64
2
+ import hashlib
2
3
  import logging
3
- from collections.abc import Iterable, Mapping
4
+ from collections.abc import Iterable, Mapping, MutableMapping
4
5
  from datetime import timedelta
5
6
  from typing import Any
6
7
 
@@ -140,7 +141,7 @@ class DynatraceTokenProviderIntegration(
140
141
  if tenant_id not in existing_dtp_tokens:
141
142
  existing_dtp_tokens[tenant_id] = (
142
143
  dt_client.get_token_ids_map_for_name_prefix(
143
- prefix="dtp-"
144
+ prefix="dtp"
144
145
  )
145
146
  )
146
147
 
@@ -172,7 +173,7 @@ class DynatraceTokenProviderIntegration(
172
173
  cluster: Cluster,
173
174
  dt_client: DynatraceClient,
174
175
  ocm_client: OCMClient,
175
- existing_dtp_tokens: Mapping[str, str],
176
+ existing_dtp_tokens: MutableMapping[str, str],
176
177
  tenant_id: str,
177
178
  token_spec: DynatraceTokenProviderTokenSpecV1,
178
179
  ) -> None:
@@ -276,12 +277,52 @@ class DynatraceTokenProviderIntegration(
276
277
  f"Patched {token_spec.name=} for {SYNCSET_AND_MANIFEST_ID} in {cluster.external_id=}."
277
278
  )
278
279
 
280
+ def scopes_hash(self, scopes: Iterable[str], length: int) -> str:
281
+ m = hashlib.sha256()
282
+ msg = ",".join(sorted(scopes))
283
+ m.update(msg.encode("utf-8"))
284
+ return m.hexdigest()[:length]
285
+
286
+ def dynatrace_token_name(self, spec: DynatraceAPITokenV1, cluster_uuid: str) -> str:
287
+ scopes_hash = self.scopes_hash(scopes=spec.scopes, length=12)
288
+ # We have a limit of 100 chars
289
+ # cluster_uuid = 36 chars
290
+ # scopes_hash = 12 chars
291
+ # prefix + separators = 6 chars
292
+ return f"dtp_{spec.name[:46]}_{cluster_uuid}_{scopes_hash}"
293
+
294
+ def sync_token_in_dynatrace(
295
+ self,
296
+ token_id: str,
297
+ spec: DynatraceAPITokenV1,
298
+ cluster_uuid: str,
299
+ dt_client: DynatraceClient,
300
+ token_name_in_dt_api: str,
301
+ dry_run: bool,
302
+ ) -> None:
303
+ """
304
+ We ensure that the given token is properly configured in Dynatrace
305
+ according to the given spec.
306
+
307
+ A list query on the tokens does not return each tokens configuration.
308
+ We encode the token configuration in the token name to save API calls.
309
+ """
310
+ expected_name = self.dynatrace_token_name(spec=spec, cluster_uuid=cluster_uuid)
311
+ if token_name_in_dt_api != expected_name:
312
+ logging.info(
313
+ f"{token_name_in_dt_api=} != {expected_name=}. Sync dynatrace token {token_id=} with {spec=} for {cluster_uuid=}."
314
+ )
315
+ if not dry_run:
316
+ dt_client.update_token(
317
+ token_id=token_id, name=expected_name, scopes=spec.scopes
318
+ )
319
+
279
320
  def generate_desired(
280
321
  self,
281
322
  dry_run: bool,
282
323
  current_k8s_secrets: Iterable[K8sSecret],
283
324
  desired_spec: DynatraceTokenProviderTokenSpecV1,
284
- existing_dtp_tokens: Mapping[str, str],
325
+ existing_dtp_tokens: MutableMapping[str, str],
285
326
  dt_client: DynatraceClient,
286
327
  cluster_uuid: str,
287
328
  ) -> tuple[bool, Iterable[K8sSecret]]:
@@ -301,15 +342,24 @@ class DynatraceTokenProviderIntegration(
301
342
  else {}
302
343
  )
303
344
  for desired_token in secret.tokens:
304
- new_token = current_tokens_by_name.get(desired_token.name)
305
- if not new_token or new_token.id not in existing_dtp_tokens:
345
+ cur_token = current_tokens_by_name.get(desired_token.name)
346
+ if not cur_token or cur_token.id not in existing_dtp_tokens:
306
347
  has_diff = True
307
348
  if not dry_run:
308
- new_token = self.create_dynatrace_token(
349
+ cur_token = self.create_dynatrace_token(
309
350
  dt_client, cluster_uuid, desired_token
310
351
  )
311
- if new_token:
312
- desired_tokens.append(new_token)
352
+ existing_dtp_tokens[cur_token.id] = cur_token.name
353
+ if cur_token:
354
+ self.sync_token_in_dynatrace(
355
+ token_id=cur_token.id,
356
+ spec=desired_token,
357
+ cluster_uuid=cluster_uuid,
358
+ dt_client=dt_client,
359
+ dry_run=dry_run,
360
+ token_name_in_dt_api=existing_dtp_tokens[cur_token.id],
361
+ )
362
+ desired_tokens.append(cur_token)
313
363
  desired.append(
314
364
  K8sSecret(
315
365
  secret_name=secret.name,
@@ -323,7 +373,7 @@ class DynatraceTokenProviderIntegration(
323
373
  def create_dynatrace_token(
324
374
  self, dt_client: DynatraceClient, cluster_uuid: str, token: DynatraceAPITokenV1
325
375
  ) -> DynatraceAPIToken:
326
- token_name = f"dtp-{token.name}-{cluster_uuid}"
376
+ token_name = self.dynatrace_token_name(spec=token, cluster_uuid=cluster_uuid)
327
377
  new_token = dt_client.create_api_token(
328
378
  name=token_name,
329
379
  scopes=token.scopes,
@@ -41,9 +41,6 @@ from reconcile.utils.external_resource_spec import (
41
41
  )
42
42
  from reconcile.utils.secret_reader import SecretReaderBase
43
43
 
44
- FLAG_RESOURCE_MANAGED_BY_ERV2 = "managed_by_erv2"
45
- FLAG_DELETE_RESOURCE = "delete"
46
-
47
44
 
48
45
  def setup_factories(
49
46
  settings: ExternalResourcesSettingsV1,
@@ -323,9 +320,7 @@ class ExternalResourcesManager:
323
320
  return resource
324
321
 
325
322
  def _serialize_resource_input(self, resource: ExternalResource) -> str:
326
- return resource.json(
327
- exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2, FLAG_DELETE_RESOURCE}}
328
- )
323
+ return resource.json()
329
324
 
330
325
  def handle_resources(self) -> None:
331
326
  desired_r = self._get_desired_objects_reconciliations()
@@ -14,3 +14,4 @@ SECRET_UPDATED_AT_TIMEFORMAT = "%Y-%m-%dT%H:%M:%SZ"
14
14
 
15
15
  FLAG_RESOURCE_MANAGED_BY_ERV2 = "managed_by_erv2"
16
16
  FLAG_DELETE_RESOURCE = "delete"
17
+ MODULE_OVERRIDES = "module_overrides"
@@ -13,13 +13,18 @@ from typing import Any
13
13
 
14
14
  from pydantic import BaseModel
15
15
 
16
+ from reconcile.external_resources.meta import (
17
+ FLAG_DELETE_RESOURCE,
18
+ FLAG_RESOURCE_MANAGED_BY_ERV2,
19
+ MODULE_OVERRIDES,
20
+ )
16
21
  from reconcile.gql_definitions.external_resources.external_resources_modules import (
17
22
  ExternalResourcesModuleV1,
18
23
  )
19
24
  from reconcile.gql_definitions.external_resources.external_resources_namespaces import (
25
+ ExternalResourcesModuleOverridesV1,
20
26
  NamespaceTerraformProviderResourceAWSV1,
21
27
  NamespaceTerraformResourceRDSV1,
22
- NamespaceTerraformResourceRoleV1,
23
28
  NamespaceV1,
24
29
  )
25
30
  from reconcile.utils.exceptions import FetchResourceError
@@ -60,30 +65,48 @@ class ExternalResourceKey(BaseModel, frozen=True):
60
65
  return f"{self.provision_provider}/{self.provisioner_name}/{self.provider}/{self.identifier}"
61
66
 
62
67
 
68
+ SUPPORTED_RESOURCE_PROVIDERS = NamespaceTerraformProviderResourceAWSV1
69
+ SUPPORTED_RESOURCE_TYPES = NamespaceTerraformResourceRDSV1
70
+
71
+
63
72
  class ExternalResourcesInventory(MutableMapping):
64
73
  _inventory: dict[ExternalResourceKey, ExternalResourceSpec] = {}
65
74
 
75
+ def _build_external_resource_spec(
76
+ self,
77
+ namespace: NamespaceV1,
78
+ provider: SUPPORTED_RESOURCE_PROVIDERS,
79
+ resource: SUPPORTED_RESOURCE_TYPES,
80
+ ) -> ExternalResourceSpec:
81
+ spec = ExternalResourceSpec(
82
+ provision_provider=provider.provider,
83
+ provisioner=provider.provisioner.dict(),
84
+ resource=resource.dict(
85
+ exclude={
86
+ FLAG_RESOURCE_MANAGED_BY_ERV2,
87
+ FLAG_DELETE_RESOURCE,
88
+ MODULE_OVERRIDES,
89
+ }
90
+ ),
91
+ namespace=namespace.dict(),
92
+ )
93
+ spec.metadata[FLAG_DELETE_RESOURCE] = resource.delete
94
+ spec.metadata[MODULE_OVERRIDES] = resource.module_overrides
95
+ return spec
96
+
66
97
  def __init__(self, namespaces: Iterable[NamespaceV1]) -> None:
67
98
  desired_providers = [
68
99
  (p, ns)
69
100
  for ns in namespaces
70
101
  for p in ns.external_resources or []
71
- if isinstance(p, NamespaceTerraformProviderResourceAWSV1) and p.resources
102
+ if isinstance(p, SUPPORTED_RESOURCE_PROVIDERS) and p.resources
72
103
  ]
73
104
 
74
105
  desired_specs = [
75
- ExternalResourceSpec(
76
- provision_provider=p.provider,
77
- provisioner=p.provisioner.dict(),
78
- resource=r.dict(),
79
- namespace=ns.dict(),
80
- )
106
+ self._build_external_resource_spec(ns, p, r)
81
107
  for (p, ns) in desired_providers
82
108
  for r in p.resources
83
- if isinstance(
84
- r, NamespaceTerraformResourceRDSV1 | NamespaceTerraformResourceRoleV1
85
- )
86
- and r.managed_by_erv2
109
+ if isinstance(r, SUPPORTED_RESOURCE_TYPES) and r.managed_by_erv2
87
110
  ]
88
111
 
89
112
  for spec in desired_specs:
@@ -181,14 +204,22 @@ class ExternalResourceModuleConfiguration(BaseModel, frozen=True):
181
204
  def resolve_configuration(
182
205
  module: ExternalResourcesModuleV1, spec: ExternalResourceSpec
183
206
  ) -> "ExternalResourceModuleConfiguration":
184
- # TODO: Modify resource schemas to include this attributes
185
- data = {
186
- "image": module.image,
187
- "version": module.default_version,
188
- "reconcile_drift_interval_minutes": module.reconcile_drift_interval_minutes,
189
- "reconcile_timeout_minutes": module.reconcile_timeout_minutes,
190
- }
191
- return ExternalResourceModuleConfiguration.parse_obj(data)
207
+ module_overrides = spec.metadata.get(
208
+ "module_overrides"
209
+ ) or ExternalResourcesModuleOverridesV1(
210
+ module_type=None,
211
+ image=None,
212
+ version=None,
213
+ reconcile_timeout_minutes=None,
214
+ )
215
+
216
+ return ExternalResourceModuleConfiguration(
217
+ image=module_overrides.image or module.image,
218
+ version=module_overrides.version or module.version,
219
+ reconcile_drift_interval_minutes=module.reconcile_drift_interval_minutes,
220
+ reconcile_timeout_minutes=module_overrides.reconcile_timeout_minutes
221
+ or module.reconcile_timeout_minutes,
222
+ )
192
223
 
193
224
 
194
225
  class Reconciliation(BaseModel, frozen=True):
@@ -25,7 +25,7 @@ query ExternalResourcesModules {
25
25
  provider
26
26
  module_type
27
27
  image
28
- default_version
28
+ version
29
29
  reconcile_drift_interval_minutes
30
30
  reconcile_timeout_minutes
31
31
  outputs_secret_sync
@@ -45,9 +45,9 @@ class ExternalResourcesModuleV1(ConfiguredBaseModel):
45
45
  provider: str = Field(..., alias="provider")
46
46
  module_type: str = Field(..., alias="module_type")
47
47
  image: str = Field(..., alias="image")
48
- default_version: str = Field(..., alias="default_version")
49
- reconcile_drift_interval_minutes: str = Field(..., alias="reconcile_drift_interval_minutes")
50
- reconcile_timeout_minutes: str = Field(..., alias="reconcile_timeout_minutes")
48
+ version: str = Field(..., alias="version")
49
+ reconcile_drift_interval_minutes: int = Field(..., alias="reconcile_drift_interval_minutes")
50
+ reconcile_timeout_minutes: int = Field(..., alias="reconcile_timeout_minutes")
51
51
  outputs_secret_sync: bool = Field(..., alias="outputs_secret_sync")
52
52
 
53
53
 
@@ -109,6 +109,12 @@ query ExternalResourcesNamespaces {
109
109
  }
110
110
  managed_by_erv2
111
111
  delete
112
+ module_overrides {
113
+ module_type
114
+ image
115
+ version
116
+ reconcile_timeout_minutes
117
+ }
112
118
  }
113
119
  ... on NamespaceTerraformResourceS3_v1 {
114
120
  region
@@ -535,6 +541,13 @@ class AWSRDSDataClassificationV1(ConfiguredBaseModel):
535
541
  loss_impact: Optional[str] = Field(..., alias="loss_impact")
536
542
 
537
543
 
544
+ class ExternalResourcesModuleOverridesV1(ConfiguredBaseModel):
545
+ module_type: Optional[str] = Field(..., alias="module_type")
546
+ image: Optional[str] = Field(..., alias="image")
547
+ version: Optional[str] = Field(..., alias="version")
548
+ reconcile_timeout_minutes: Optional[int] = Field(..., alias="reconcile_timeout_minutes")
549
+
550
+
538
551
  class NamespaceTerraformResourceRDSV1(NamespaceTerraformResourceAWSV1):
539
552
  region: Optional[str] = Field(..., alias="region")
540
553
  identifier: str = Field(..., alias="identifier")
@@ -554,6 +567,7 @@ class NamespaceTerraformResourceRDSV1(NamespaceTerraformResourceAWSV1):
554
567
  data_classification: Optional[AWSRDSDataClassificationV1] = Field(..., alias="data_classification")
555
568
  managed_by_erv2: Optional[bool] = Field(..., alias="managed_by_erv2")
556
569
  delete: Optional[bool] = Field(..., alias="delete")
570
+ module_overrides: Optional[ExternalResourcesModuleOverridesV1] = Field(..., alias="module_overrides")
557
571
 
558
572
 
559
573
  class AWSS3EventNotificationV1(ConfiguredBaseModel):
@@ -88,13 +88,13 @@ class ExternalResourceSpec:
88
88
  secret: Mapping[str, str] = field(init=False, default_factory=lambda: {})
89
89
  # Metadata is used for processing data that shuold not be included in the secret data
90
90
  # e.g: ERV2 adds a updated_at attribute that acts as optimistic lock.
91
- metadata: MutableMapping[str, str] = field(
91
+ metadata: MutableMapping[str, Any] = field(
92
92
  init=False, compare=False, repr=False, hash=False, default_factory=lambda: {}
93
93
  )
94
94
 
95
95
  @property
96
96
  def marked_to_delete(self) -> bool:
97
- return self.resource.get("delete") or False
97
+ return self.metadata.get("delete") or False
98
98
 
99
99
  @property
100
100
  def provider(self) -> str:
tools/qontract_cli.py CHANGED
@@ -60,9 +60,9 @@ from reconcile.external_resources.integration import (
60
60
  get_aws_api,
61
61
  )
62
62
  from reconcile.external_resources.manager import (
63
- FLAG_RESOURCE_MANAGED_BY_ERV2,
64
63
  setup_factories,
65
64
  )
65
+ from reconcile.external_resources.meta import FLAG_RESOURCE_MANAGED_BY_ERV2
66
66
  from reconcile.external_resources.model import (
67
67
  ExternalResourceKey,
68
68
  ExternalResourcesInventory,