qontract-reconcile 0.10.1rc1173__py3-none-any.whl → 0.10.1rc1175__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.1rc1173
3
+ Version: 0.10.1rc1175
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
@@ -192,7 +192,7 @@ reconcile/endpoints_discovery/integration.py,sha256=znfnlm8bZesfcNbQnaR2aaVM-DTB
192
192
  reconcile/endpoints_discovery/merge_request.py,sha256=_yLb4tnvoZMCko8rta2C_CvOInJa9pa3HzSmHNtjgGU,2978
193
193
  reconcile/endpoints_discovery/merge_request_manager.py,sha256=wUMsumxv8RnWaRattax4HfoRlhtVzmgro3GiJJ1C4Vc,6392
194
194
  reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
195
- reconcile/external_resources/aws.py,sha256=309Zui7rE8XFJA1ZBLupl55Vp8Y5KKgXdsKQWqKbg8I,7069
195
+ reconcile/external_resources/aws.py,sha256=EN2Vz6IRp6TNWX5vwGAGESrD09_8sTFzKgZjdDR6cmg,7072
196
196
  reconcile/external_resources/factories.py,sha256=KrJDh52_8PeCEVjwfeVr1jwAJDdhMXRQ_XcBETfnKY4,4988
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
@@ -200,7 +200,7 @@ reconcile/external_resources/manager.py,sha256=fXUm09w-9FRWYfJAwtV4_td1UHNLzdoGh
200
200
  reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
201
201
  reconcile/external_resources/metrics.py,sha256=nMbyonGZEJDD1lYzpQY2eR9TNwvxYC4ZCcpi6wrExcM,1037
202
202
  reconcile/external_resources/model.py,sha256=H3elpiqehg_jACy28fGV5_77n8gKclVO77-7cfbaMNA,9178
203
- reconcile/external_resources/reconciler.py,sha256=Lhzg0O9Sw65KrCae-J14g9PV82xdp29O4jS_T5YNgEY,9661
203
+ reconcile/external_resources/reconciler.py,sha256=K9QvbQCIOCuOHnPIxQE_P_jFtrkF3dGo8d_cCCh08Ys,8973
204
204
  reconcile/external_resources/secrets_sync.py,sha256=6n0oDPLjd9Ql0lf6zsr1AZw8A6EEe3yCzl20XodtgkE,16229
205
205
  reconcile/external_resources/state.py,sha256=z086bnIUTOkzFmQvS9rSAhFsM3Aw_9PLKHBACJ-0tQc,9690
206
206
  reconcile/glitchtip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -838,11 +838,11 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
838
838
  tools/app_interface_reporter.py,sha256=oZPib4HPq0aZ2Zui1QGJGk6qQdfpeihujGDBnSdKyGE,17627
839
839
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
840
840
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
841
- tools/qontract_cli.py,sha256=ndRUc8mjkubajPkZKaoE2IFVT2e4YXYRVtUaeLt3zzE,140496
841
+ tools/qontract_cli.py,sha256=YMOYkmP9WZFMX8wPxoJZ5ddstK3YyXMMx6ReXnCiH0w,140707
842
842
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
843
843
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
844
844
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
845
- tools/cli_commands/erv2.py,sha256=fByRn6D_SBi5YptjAqR49yFedsjyAHBQBaIbsdO1QKQ,16412
845
+ tools/cli_commands/erv2.py,sha256=469qdhyaf7thpPQ4hJSurvmxBqYDJsoI8H4AigQIF7U,20737
846
846
  tools/cli_commands/gpg_encrypt.py,sha256=x02JOMn834z89YSNvr5B-oJky7rR1C0begCkPh45eHk,4958
847
847
  tools/cli_commands/systems_and_tools.py,sha256=EMHOF1AtUDaoSk0bbjl6oUKYAz4rTZjIBaF-6E6GspM,16816
848
848
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -880,8 +880,8 @@ tools/test/test_qontract_cli.py,sha256=iuzKbQ6ahinvjoQmQLBrG4shey0z-1rB6qCgS8T6d
880
880
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
881
881
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
882
882
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
883
- qontract_reconcile-0.10.1rc1173.dist-info/METADATA,sha256=UAjgR0lD9syc08q6s2PuH2Q3_cOi0HN8W2Q7fqJcLh8,2213
884
- qontract_reconcile-0.10.1rc1173.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
885
- qontract_reconcile-0.10.1rc1173.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
- qontract_reconcile-0.10.1rc1173.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
- qontract_reconcile-0.10.1rc1173.dist-info/RECORD,,
883
+ qontract_reconcile-0.10.1rc1175.dist-info/METADATA,sha256=W7cOYjf2juRnK6QmkGQqjd_MG3Sft1hMo5imMLED1qo,2213
884
+ qontract_reconcile-0.10.1rc1175.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
885
+ qontract_reconcile-0.10.1rc1175.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
+ qontract_reconcile-0.10.1rc1175.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
+ qontract_reconcile-0.10.1rc1175.dist-info/RECORD,,
@@ -9,7 +9,6 @@ from reconcile.utils.external_resource_spec import (
9
9
  ExternalResourceSpec,
10
10
  )
11
11
  from reconcile.utils.external_resources import ResourceValueResolver
12
- from reconcile.utils.helpers import generate_random_password
13
12
  from reconcile.utils.secret_reader import SecretReaderBase
14
13
 
15
14
 
@@ -59,10 +58,6 @@ class AWSElasticacheFactory(AWSDefaultResourceFactory):
59
58
  pg_data = rvr._get_values(data["parameter_group"])
60
59
  data["parameter_group"] = pg_data
61
60
 
62
- if data.get("transit_encryption_enabled", False):
63
- data["auth_token"] = (
64
- spec.get_secret_field("db.auth_token") or generate_random_password()
65
- )
66
61
  return data
67
62
 
68
63
  def validate(self, resource: ExternalResource) -> None:
@@ -71,6 +66,11 @@ class AWSElasticacheFactory(AWSDefaultResourceFactory):
71
66
  if data.get("parameter_group"):
72
67
  if not data["parameter_group"].get("name"):
73
68
  data["parameter_group"]["name"] = f"{data['replication_group_id']}-pg"
69
+ else:
70
+ # prefix the parameter_group name with the replication_group_id
71
+ data["parameter_group"]["name"] = (
72
+ f"{data['replication_group_id']}-{data['parameter_group']['name']}"
73
+ )
74
74
 
75
75
  if (
76
76
  data.get("parameter_group_name")
@@ -21,6 +21,7 @@ from kubernetes.client import (
21
21
  from pydantic import BaseModel
22
22
 
23
23
  from reconcile.external_resources.model import (
24
+ Action,
24
25
  Reconciliation,
25
26
  )
26
27
  from reconcile.external_resources.state import ReconcileStatus
@@ -88,6 +89,75 @@ class ReconciliationK8sJob(K8sJob, BaseModel, frozen=True):
88
89
  }
89
90
 
90
91
  def job_spec(self) -> V1JobSpec:
92
+ job_container = V1Container(
93
+ name="job",
94
+ image=self.reconciliation.module_configuration.image_version,
95
+ image_pull_policy="Always",
96
+ env=[
97
+ V1EnvVar(
98
+ name="DRY_RUN",
99
+ value=str(self.is_dry_run),
100
+ ),
101
+ V1EnvVar(
102
+ name="ACTION",
103
+ value=self.reconciliation.action.value,
104
+ ),
105
+ ],
106
+ volume_mounts=[
107
+ V1VolumeMount(
108
+ name="credentials",
109
+ mount_path="/credentials",
110
+ sub_path="credentials",
111
+ ),
112
+ V1VolumeMount(
113
+ name="workdir",
114
+ mount_path="/work",
115
+ ),
116
+ self.scripts_volume_mount("/inputs"),
117
+ ],
118
+ )
119
+ outputs_secret_container = V1Container(
120
+ name="outputs",
121
+ image=self.reconciliation.module_configuration.outputs_secret_image_version,
122
+ image_pull_policy="Always",
123
+ env=[
124
+ V1EnvVar(
125
+ name="NAMESPACE",
126
+ value_from=V1EnvVarSource(
127
+ field_ref=V1ObjectFieldSelector(field_path="metadata.namespace")
128
+ ),
129
+ ),
130
+ V1EnvVar(
131
+ name="ACTION",
132
+ value=self.reconciliation.action,
133
+ ),
134
+ V1EnvVar(
135
+ name="DRY_RUN",
136
+ value=str(self.is_dry_run),
137
+ ),
138
+ ],
139
+ volume_mounts=[
140
+ V1VolumeMount(
141
+ name="credentials",
142
+ mount_path="/.aws/credentials",
143
+ sub_path="credentials",
144
+ ),
145
+ V1VolumeMount(
146
+ name="workdir",
147
+ mount_path="/work",
148
+ ),
149
+ self.scripts_volume_mount("/inputs"),
150
+ ],
151
+ )
152
+
153
+ # For delete actions, we don't need to run the outputs_secrets container
154
+ if self.reconciliation.action == Action.APPLY:
155
+ init_containers = [job_container]
156
+ containers = [outputs_secret_container]
157
+ else:
158
+ init_containers = []
159
+ containers = [job_container]
160
+
91
161
  return V1JobSpec(
92
162
  backoff_limit=0,
93
163
  active_deadline_seconds=self.reconciliation.module_configuration.reconcile_timeout_minutes
@@ -98,72 +168,8 @@ class ReconciliationK8sJob(K8sJob, BaseModel, frozen=True):
98
168
  annotations=self.annotations(), labels=self.labels()
99
169
  ),
100
170
  spec=V1PodSpec(
101
- init_containers=[
102
- V1Container(
103
- name="job",
104
- image=self.reconciliation.module_configuration.image_version,
105
- image_pull_policy="Always",
106
- env=[
107
- V1EnvVar(
108
- name="DRY_RUN",
109
- value=str(self.is_dry_run),
110
- ),
111
- V1EnvVar(
112
- name="ACTION",
113
- value=self.reconciliation.action.value,
114
- ),
115
- ],
116
- volume_mounts=[
117
- V1VolumeMount(
118
- name="credentials",
119
- mount_path="/credentials",
120
- sub_path="credentials",
121
- ),
122
- V1VolumeMount(
123
- name="workdir",
124
- mount_path="/work",
125
- ),
126
- self.scripts_volume_mount("/inputs"),
127
- ],
128
- )
129
- ],
130
- containers=[
131
- V1Container(
132
- name="outputs",
133
- image=self.reconciliation.module_configuration.outputs_secret_image_version,
134
- image_pull_policy="Always",
135
- env=[
136
- V1EnvVar(
137
- name="NAMESPACE",
138
- value_from=V1EnvVarSource(
139
- field_ref=V1ObjectFieldSelector(
140
- field_path="metadata.namespace"
141
- )
142
- ),
143
- ),
144
- V1EnvVar(
145
- name="ACTION",
146
- value=self.reconciliation.action,
147
- ),
148
- V1EnvVar(
149
- name="DRY_RUN",
150
- value=str(self.is_dry_run),
151
- ),
152
- ],
153
- volume_mounts=[
154
- V1VolumeMount(
155
- name="credentials",
156
- mount_path="/.aws/credentials",
157
- sub_path="credentials",
158
- ),
159
- V1VolumeMount(
160
- name="workdir",
161
- mount_path="/work",
162
- ),
163
- self.scripts_volume_mount("/inputs"),
164
- ],
165
- )
166
- ],
171
+ init_containers=init_containers,
172
+ containers=containers,
167
173
  image_pull_secrets=[V1LocalObjectReference(name="quay.io")],
168
174
  volumes=[
169
175
  V1Volume(
@@ -4,12 +4,12 @@ import json
4
4
  import os
5
5
  import sys
6
6
  from collections.abc import Iterator
7
- from contextlib import contextmanager
7
+ from contextlib import contextmanager, suppress
8
8
  from difflib import get_close_matches
9
9
  from enum import Enum
10
10
  from pathlib import Path
11
11
  from subprocess import CalledProcessError, run
12
- from typing import Protocol
12
+ from typing import Any, Protocol
13
13
 
14
14
  from pydantic import BaseModel
15
15
  from rich import print as rich_print
@@ -178,6 +178,7 @@ class Erv2Cli:
178
178
 
179
179
  # run cdktf synth
180
180
  with task(self.progress_spinner, "-- Running CDKTF synth"):
181
+ run(["docker", "pull", self.image], check=True, capture_output=True)
181
182
  run(
182
183
  [
183
184
  "docker",
@@ -245,8 +246,14 @@ class TfAction(Enum):
245
246
  DESTROY = "delete"
246
247
 
247
248
 
249
+ class Change(BaseModel):
250
+ before: Any | None = None
251
+ after: Any | None = None
252
+
253
+
248
254
  class TfResource(BaseModel):
249
255
  address: str
256
+ change: Change | None = None
250
257
 
251
258
  @property
252
259
  def id(self) -> str:
@@ -269,13 +276,19 @@ class TfResourceList(BaseModel):
269
276
  def __iter__(self) -> Iterator[TfResource]: # type: ignore
270
277
  return iter(self.resources)
271
278
 
272
- def _get_resource_by_address(self, address: str) -> TfResource | None:
279
+ def get_resource_by_address(self, address: str) -> TfResource | None:
273
280
  for resource in self.resources:
274
281
  if resource.address == address:
275
282
  return resource
276
283
  return None
277
284
 
278
- def _get_resources_by_type(self, type: str) -> list[TfResource]:
285
+ def get_resource_by_type(self, type: str) -> TfResource:
286
+ results = self.get_resources_by_type(type)
287
+ if len(results) > 1:
288
+ raise ValueError(f"More than one resource found for type '{type}'!")
289
+ return results[0]
290
+
291
+ def get_resources_by_type(self, type: str) -> list[TfResource]:
279
292
  results = [resource for resource in self.resources if resource.type == type]
280
293
  if not results:
281
294
  raise KeyError(f"Resource type '{type}' not found!")
@@ -287,13 +300,13 @@ class TfResourceList(BaseModel):
287
300
  self holds the source resources (terraform-resources).
288
301
  The tf_resource is the destination resource (ERv2).
289
302
  """
290
- if resource := self._get_resource_by_address(tf_resource.address):
303
+ if resource := self.get_resource_by_address(tf_resource.address):
291
304
  # exact match by AWS address
292
305
  return [resource]
293
306
 
294
307
  # a resource with the same ID does not exist
295
308
  # let's try to find the resource by the AWS type
296
- results = self._get_resources_by_type(tf_resource.type)
309
+ results = self.get_resources_by_type(tf_resource.type)
297
310
  if len(results) == 1:
298
311
  # there is just one resource with the same type
299
312
  # this must be the searched resource.
@@ -354,6 +367,25 @@ class TerraformCli:
354
367
  if not self._dry_run:
355
368
  self._tf_run(self._path, ["state", "push", str(self.state_file)])
356
369
 
370
+ def _tf_import(self, address: str, value: str) -> None:
371
+ """Import a resource.
372
+
373
+ !!! Attention !!!
374
+
375
+ Because terraform import doesn't use the local state file and always imports the resource to the remote state,
376
+ we need to push and pull the state file again.
377
+ """
378
+ # push local changes
379
+ self._tf_state_push()
380
+ with task(
381
+ self.progress_spinner,
382
+ f"-- Importing resource {address} {'[b red](DRY-RUN)' if self._dry_run else ''}",
383
+ ):
384
+ if not self._dry_run:
385
+ self._tf_run(self._path, ["import", address, value])
386
+ # and pull the state file again
387
+ self._tf_state_pull()
388
+
357
389
  def upload_state(self) -> None:
358
390
  self._tf_state_push()
359
391
 
@@ -362,7 +394,10 @@ class TerraformCli:
362
394
  plan = json.loads(self._tf_run(self._path, ["show", "-json", "plan.out"]))
363
395
  return TfResourceList(
364
396
  resources=[
365
- TfResource(address=r["address"])
397
+ TfResource(
398
+ address=r["address"],
399
+ change=Change(**r["change"]),
400
+ )
366
401
  for r in plan["resource_changes"]
367
402
  if action.value.lower() in r["change"]["actions"]
368
403
  ]
@@ -372,24 +407,112 @@ class TerraformCli:
372
407
  self, source_state_file: Path, source: TfResource, destination: TfResource
373
408
  ) -> None:
374
409
  """Move the resource from source state file to destination state file."""
375
- if self.progress_spinner:
376
- self.progress_spinner.log(
377
- f"-- Moving {destination} {'[b red](DRY-RUN)' if self._dry_run else ''}"
378
- )
410
+ with task(
411
+ self.progress_spinner,
412
+ f"-- Moving {destination} {'[b red](DRY-RUN)' if self._dry_run else ''}",
413
+ ):
414
+ if not self._dry_run:
415
+ self._tf_run(
416
+ self._path,
417
+ [
418
+ "state",
419
+ "mv",
420
+ "-lock=false",
421
+ f"-state={source_state_file!s}",
422
+ f"-state-out={self.state_file!s}",
423
+ f"{source.address}",
424
+ f"{destination.address}",
425
+ ],
426
+ )
427
+
428
+ def commit(self, source: TerraformCli) -> None:
429
+ """Commit the changes."""
379
430
  if not self._dry_run:
380
- self._tf_run(
381
- self._path,
382
- [
383
- "state",
384
- "mv",
385
- "-lock=false",
386
- f"-state={source_state_file!s}",
387
- f"-state-out={self.state_file!s}",
388
- f"{source.address}",
389
- f"{destination.address}",
390
- ],
431
+ if self.progress_spinner:
432
+ self.progress_spinner.stop()
433
+ if not Confirm.ask(
434
+ "\nEverything ok? Would you like to upload the modified terraform states",
435
+ default=False,
436
+ ):
437
+ return
438
+
439
+ if self.progress_spinner:
440
+ self.progress_spinner.start()
441
+
442
+ # finally push the terraform states
443
+ self.upload_state()
444
+ source.upload_state()
445
+
446
+ def _elasticache_import_password(
447
+ self, destination: TfResource, password: str
448
+ ) -> None:
449
+ """Import the elasticache auth_token random_password."""
450
+ self._tf_import(address=destination.address, value=password)
451
+
452
+ if (
453
+ not destination.change
454
+ or not destination.change.after
455
+ or not destination.change.after.get("override_special")
456
+ ):
457
+ # nothing to change, nothing to do
458
+ return
459
+
460
+ state_data = json.loads(self.state_file.read_text())
461
+ state_data["serial"] += 1
462
+ if self._dry_run:
463
+ # in dry-run mode, tf_import is a no-op, therefore, the password is not in the state yet.
464
+ return
465
+ state_password_obj = next(
466
+ r for r in state_data["resources"] if r["name"] == destination.id
467
+ )
468
+
469
+ # Set the "override_special" to disable the password reset
470
+ state_password_obj["instances"][0]["attributes"]["override_special"] = (
471
+ destination.change.after["override_special"]
472
+ )
473
+ # Write the state,
474
+ self.state_file.write_text(json.dumps(state_data, indent=2))
475
+
476
+ def migrate_elasticache_resources(self, source: TerraformCli) -> None:
477
+ source_resources = source.resource_changes(TfAction.DESTROY)
478
+ destination_resources = self.resource_changes(TfAction.CREATE)
479
+ if not source_resources or not destination_resources:
480
+ raise ValueError("No resource changes found!")
481
+
482
+ source_ec = source_resources.get_resource_by_type(
483
+ "aws_elasticache_replication_group"
484
+ )
485
+ if not source_ec.change or not source_ec.change.before:
486
+ raise ValueError(
487
+ "Something went wrong with the source elasticache instance!"
391
488
  )
392
489
 
490
+ current_auth_token = source_ec.change.before.get("auth_token")
491
+ if current_auth_token:
492
+ with suppress(KeyError):
493
+ self._elasticache_import_password(
494
+ destination_resources.get_resource_by_type("random_password"),
495
+ current_auth_token,
496
+ )
497
+
498
+ # migrate resources
499
+ for destination_resource in destination_resources:
500
+ if current_auth_token and destination_resource.type == "random_password":
501
+ # random password handled above
502
+ continue
503
+
504
+ possible_source_resouces = source_resources[destination_resource]
505
+ if not possible_source_resouces or len(possible_source_resouces) > 1:
506
+ raise ValueError(
507
+ f"Either source resource for {destination_resource} not found or more than one resource found!"
508
+ )
509
+ self.move_resource(
510
+ source_state_file=source.state_file,
511
+ source=possible_source_resouces[0],
512
+ destination=destination_resource,
513
+ )
514
+ self.commit(source)
515
+
393
516
  def migrate_resources(self, source: TerraformCli) -> None:
394
517
  """Migrate the resources from source."""
395
518
  # if not self.initialized or not source.initialized:
@@ -456,18 +579,4 @@ class TerraformCli:
456
579
  destination=destination_resource,
457
580
  )
458
581
 
459
- if not self._dry_run:
460
- if self.progress_spinner:
461
- self.progress_spinner.stop()
462
- if not Confirm.ask(
463
- "\nEverything ok? Would you like to upload the modified terraform states",
464
- default=False,
465
- ):
466
- return
467
-
468
- if self.progress_spinner:
469
- self.progress_spinner.start()
470
-
471
- # finally push the terraform states
472
- self.upload_state()
473
- source.upload_state()
582
+ self.commit(source)
tools/qontract_cli.py CHANGED
@@ -4304,7 +4304,11 @@ def migrate(ctx, dry_run: bool, skip_build: bool) -> None:
4304
4304
  progress,
4305
4305
  "Migrating the resources from terraform-resources to ERv2",
4306
4306
  ):
4307
- erv2_tf_cli.migrate_resources(source=tfr_tf_cli)
4307
+ if ctx.obj["provider"] == "elasticache":
4308
+ # Elasticache migration is a bit different
4309
+ erv2_tf_cli.migrate_elasticache_resources(source=tfr_tf_cli)
4310
+ else:
4311
+ erv2_tf_cli.migrate_resources(source=tfr_tf_cli)
4308
4312
 
4309
4313
  rich_print(f"[b red]Please remove the temporary directory ({tempdir}) manually!")
4310
4314