qontract-reconcile 0.10.2.dev476__py3-none-any.whl → 0.10.2.dev478__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.dev476
3
+ Version: 0.10.2.dev478
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
@@ -186,14 +186,15 @@ reconcile/endpoints_discovery/merge_request.py,sha256=vKcfcSLodR_q1iQxFjEceHA-OT
186
186
  reconcile/endpoints_discovery/merge_request_manager.py,sha256=hFq-Dxw7IDB2D34zYMQPKM8Lh-PznklQE5LUSye1VJA,6297
187
187
  reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
188
188
  reconcile/external_resources/aws.py,sha256=kh5p9KsYB7naewVPLPtcCjnIrUECmdR6lUgDrzIlL1k,11985
189
- reconcile/external_resources/factories.py,sha256=ftHFxEvzFp-oAOOLeR9viTLLVz0g6Hgs8ZNEsJKKOcs,5903
189
+ reconcile/external_resources/cloudflare.py,sha256=7Yq31L_enR-iyAAsVx_Z-QVbRjh_GmFyuxVBCTej5a8,2426
190
+ reconcile/external_resources/factories.py,sha256=52GzEmEaLhriG0AQzHh0R8n3IwnGvwpxVCv8U0glGC4,8506
190
191
  reconcile/external_resources/integration.py,sha256=Fcc7MjixGx6bE6K4fgXx6yFomVoJavsOLhhDgL7VJgg,7040
191
192
  reconcile/external_resources/integration_secrets_sync.py,sha256=M2uOFi2JXWhiw3hQyE_4NVPQmPIYGHlghRti5eLWhkw,1738
192
- reconcile/external_resources/manager.py,sha256=3CNpp-QHR3d-A4C4wMj4KvrAG0lWTwW5jcFA8AR-g4w,18363
193
+ reconcile/external_resources/manager.py,sha256=dwDFqvN5GDnmSKBHVxQt2Wb4hspJQiS7gyITylA2Y8U,18856
193
194
  reconcile/external_resources/meta.py,sha256=RM8qGE6UoJR58nLqABReIJrrdmx4Tqt_OcdlT-QygGs,620
194
195
  reconcile/external_resources/metrics.py,sha256=ahvlgrc48B214NwBq-G6ncwQE3Qrtif61jtOrklfylQ,3903
195
- reconcile/external_resources/model.py,sha256=Kfwxm9TLa6j7Vs-rxXQTIQoNmJPHilSJbjLdlQmNZc4,15172
196
- reconcile/external_resources/reconciler.py,sha256=hb32ERLhxkNR-RRYWaE0ngjKjHBM8uarBy8bDVPGGmE,9732
196
+ reconcile/external_resources/model.py,sha256=8kw2-Y0DxHpdNxaxdfM6xD_cyS40MElTHgfD-Sm2j2E,15405
197
+ reconcile/external_resources/reconciler.py,sha256=-jXny_rFnUpPs_VFbA0Iig37-icOu-9i6R-KXjPuWb8,10150
197
198
  reconcile/external_resources/secrets_sync.py,sha256=vdcy2Ydz67pKkSHT2U0-Ucu9cxMr99r15RovLTxmoZU,16162
198
199
  reconcile/external_resources/state.py,sha256=zWla2_j6BI1FBrSPqRRfuo55bXK0QWAxtEeafb8ffTg,13811
199
200
  reconcile/fleet_labeler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -214,7 +215,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=prje61EOuLEIZLLxlJS_YN0
214
215
  reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
215
216
  reconcile/glitchtip_project_dsn/integration.py,sha256=3GgcqUM6hWhLpo9Yx5Xr9vrdexF-WNevVCNL9bJ0Upc,8162
216
217
  reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
217
- reconcile/gql_definitions/introspection.json,sha256=liDjRAOJ0_ZN7bagOacrd1yGlybaxK8V9rQTflnecxo,2429961
218
+ reconcile/gql_definitions/introspection.json,sha256=FjXblj7vGdpsRA1RkrIQyXQ2t9B60ERqHIt7WRKBoGg,2432241
218
219
  reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
219
220
  reconcile/gql_definitions/acs/acs_instances.py,sha256=VySMcnWddg-jXj-bj_ddLIwLX3u1GSFUm02H8rJDBYU,2167
220
221
  reconcile/gql_definitions/acs/acs_policies.py,sha256=jEV1U8j4VYL9ih17JSK1tiz2s_1CegVECmXU-NVEQvA,4333
@@ -307,7 +308,7 @@ reconcile/gql_definitions/endpoints_discovery/apps.py,sha256=p3hvzvrtkCCQfQoJ3mi
307
308
  reconcile/gql_definitions/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
309
  reconcile/gql_definitions/external_resources/aws_accounts.py,sha256=bRzfuPDLJvVJRx7IzqAJKnqpd7SBWdj3trI1rNPeYnU,2033
309
310
  reconcile/gql_definitions/external_resources/external_resources_modules.py,sha256=w07PFh526GaYnZRe-SH92MaxA-aeD2TDT2kG_3Da_HE,3241
310
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py,sha256=DGKKoJK7rOng2FBan8vxunLdlysX9Hb9_GTPsm2wyf8,46616
311
+ reconcile/gql_definitions/external_resources/external_resources_namespaces.py,sha256=kb6-sHCAZ6-mW-4SGeLWex-MGQ69VkxpTsJQj1L-__Q,48172
311
312
  reconcile/gql_definitions/external_resources/external_resources_settings.py,sha256=RvAMgipgH3MoLfWaqCPaYUy8GS3v0Dr4Cod17OsaNx0,3567
312
313
  reconcile/gql_definitions/external_resources/fragments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
313
314
  reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py,sha256=jjABgAhVx7LO3NJd9l90JH8s-_TJFvduBZRbdVquLfY,1313
@@ -417,7 +418,7 @@ reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_
417
418
  reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py,sha256=F9A6csePJZNUxr0N7-8CC_RcZ4RPMdNwuFMaBtXZqzQ,5817
418
419
  reconcile/gql_definitions/terraform_cloudflare_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
419
420
  reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py,sha256=Wd6i1oo5WxlyXHeab2GuyXL8xgpm1NXCphCyMdQ2MLw,3707
420
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py,sha256=4oLz5UbTbQJPnZ_X-pCzUafvgSH2f0JzMUnmfeFx_sg,12244
421
+ reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py,sha256=btJaFYHp1jxOXEsScRTIgLDlgoyytTdFBFBHvH7ymjY,12257
421
422
  reconcile/gql_definitions/terraform_cloudflare_users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
422
423
  reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py,sha256=gRxCfy4X9otRyOHtTo038gdYYD3lJ6FTz0yiGavGbfY,1956
423
424
  reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py,sha256=CFYS6kM0q3Wqrsyr7zhCLfeAkVK_Z6pD5xmkMwOyrG4,4012
@@ -665,7 +666,7 @@ reconcile/utils/terraform_client.py,sha256=VexlnYM9h-2-g6Y8HiE48BFi8AX-mYLhIapl2
665
666
  reconcile/utils/terrascript_aws_client.py,sha256=qazXB2Re1DFReDcHwplcIF7_SmlE__z0XJv6hvd0L50,307485
666
667
  reconcile/utils/three_way_diff_strategy.py,sha256=yDEbP3HWvDDVzo_8FEbcT0pA6lz72HviXZkh5wmzkkY,4837
667
668
  reconcile/utils/throughput.py,sha256=KNDCVsCLSp89V4pO3sEUd7bJUuh6gNfsxsc_18rEv_Y,357
668
- reconcile/utils/vault.py,sha256=e5zo-PmqG6ccp87-Ip6d3_RPZ1eZEtawS1WbRAVP73Y,15366
669
+ reconcile/utils/vault.py,sha256=6gYe-EKrYnz3SI_JM8gSpbXT9yfMrwDAw23kgiH7pV4,15573
669
670
  reconcile/utils/vcs.py,sha256=RDqe4bz01n_oU4VJRjSbbmWn-yqvFNCKCkC8JXgSfuU,10304
670
671
  reconcile/utils/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
671
672
  reconcile/utils/acs/base.py,sha256=G10mrztmTbdwcSuuXu4cp5gGH2Ogv9vZAAOBQUdl7e0,2625
@@ -776,11 +777,11 @@ tools/app_sre_tekton_access_reporter.py,sha256=5qmkevJdlb2j_lpGC5Pu1Pmo0eomX5Zxz
776
777
  tools/app_sre_tekton_access_revalidation.py,sha256=vwL1o_j7oSTOhrHNH1znpgjA2LHGzb8yc5iG3aaY4m0,2684
777
778
  tools/glitchtip_access_reporter.py,sha256=wnaiDGW4MkYONV_erltnJ6nGkEj0kQrAiv04NNnOS0k,2859
778
779
  tools/glitchtip_access_revalidation.py,sha256=jjeLO53LTbz_LfQw3G2Cs8lVLO_6xqU39BYyTH3cEPE,2764
779
- tools/qontract_cli.py,sha256=DZU_DuN28IdqIZCykD06-Ze-zSrE9aKGU3a1ZEFIUIk,160262
780
+ tools/qontract_cli.py,sha256=70fp6gxuEjvEDooqThUyPV6AuXk2UjCFq9rp3-i6bUw,160266
780
781
  tools/template_validation.py,sha256=imLeY5zYE_ITk0yl99pqkoqcG2psFG0jLYKTZ7NXLcY,3393
781
782
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
782
783
  tools/cli_commands/container_images_report.py,sha256=8mAjCS6XR0yD7k0mfiVBlt6xbYU47q_ftdYNi5o5VKE,5566
783
- tools/cli_commands/erv2.py,sha256=LTZ_9aZVEonTD65Rbd5BpkK1loh3WemmyX4xJBj6YwI,26081
784
+ tools/cli_commands/erv2.py,sha256=0qx-BZJex4SUNpnSxlIkr3r7buOzjmMHhmB46C9cnJ0,24051
784
785
  tools/cli_commands/gpg_encrypt.py,sha256=EzCR3JvlCfO-a8VLG-mArD3edwM-aa0mUrDj1y0_NW0,5032
785
786
  tools/cli_commands/systems_and_tools.py,sha256=cBSHPOzjWhbYVFr50zsre5vGcVnovFkrZVSGsssUgqU,16985
786
787
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -802,7 +803,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
802
803
  tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
803
804
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
804
805
  tools/sre_checkpoints/util.py,sha256=KcYVfa3UmJHVP_ocgrKe8NkrO5IDB9aWEDydSokPcRk,975
805
- qontract_reconcile-0.10.2.dev476.dist-info/METADATA,sha256=mAm_YQtL6ZrhALV1PX-EnnQmdunNb3rFar3g0qbKxyU,24948
806
- qontract_reconcile-0.10.2.dev476.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
807
- qontract_reconcile-0.10.2.dev476.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
808
- qontract_reconcile-0.10.2.dev476.dist-info/RECORD,,
806
+ qontract_reconcile-0.10.2.dev478.dist-info/METADATA,sha256=vyQsjxw1NoAHqnLG1q2_-8QMpttGry95GFqp6ZdtNJc,24948
807
+ qontract_reconcile-0.10.2.dev478.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
808
+ qontract_reconcile-0.10.2.dev478.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
809
+ qontract_reconcile-0.10.2.dev478.dist-info/RECORD,,
@@ -0,0 +1,80 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ from reconcile.external_resources.model import (
5
+ ExternalResource,
6
+ ExternalResourceKey,
7
+ ExternalResourceModuleConfiguration,
8
+ ExternalResourcesInventory,
9
+ )
10
+ from reconcile.utils.exceptions import SecretIncompleteError
11
+ from reconcile.utils.external_resource_spec import (
12
+ ExternalResourceSpec,
13
+ )
14
+ from reconcile.utils.external_resources import ResourceValueResolver
15
+ from reconcile.utils.secret_reader import SecretReaderBase
16
+
17
+
18
+ class CloudflareResourceFactory(ABC):
19
+ def __init__(
20
+ self, er_inventory: ExternalResourcesInventory, secret_reader: SecretReaderBase
21
+ ):
22
+ self.er_inventory = er_inventory
23
+ self.secret_reader = secret_reader
24
+
25
+ @abstractmethod
26
+ def resolve(
27
+ self,
28
+ spec: ExternalResourceSpec,
29
+ module_conf: ExternalResourceModuleConfiguration,
30
+ ) -> dict[str, Any]: ...
31
+
32
+ @abstractmethod
33
+ def validate(
34
+ self,
35
+ resource: ExternalResource,
36
+ module_conf: ExternalResourceModuleConfiguration,
37
+ ) -> None: ...
38
+
39
+ def find_linked_resources(
40
+ self, spec: ExternalResourceSpec
41
+ ) -> set[ExternalResourceKey]:
42
+ """Method to find dependant resources. Resources in this list
43
+ will be reconciled every time the parent resource finishes its reconciliation."""
44
+ return set()
45
+
46
+
47
+ def get_account_id(
48
+ secret_reader: SecretReaderBase, api_credentials: dict[str, Any]
49
+ ) -> str:
50
+ creds = secret_reader.read_all(api_credentials)
51
+ account_id = creds.get("account_id")
52
+ if not account_id:
53
+ raise SecretIncompleteError(
54
+ f"secret {api_credentials['path']} incomplete: account_id missing"
55
+ )
56
+ return account_id
57
+
58
+
59
+ class CloudflareDefaultResourceFactory(CloudflareResourceFactory):
60
+ def resolve(
61
+ self,
62
+ spec: ExternalResourceSpec,
63
+ module_conf: ExternalResourceModuleConfiguration,
64
+ ) -> dict[str, Any]:
65
+ account_id = get_account_id(
66
+ self.secret_reader,
67
+ spec.provisioner["api_credentials"],
68
+ )
69
+ resolved_values = ResourceValueResolver(
70
+ spec=spec, identifier_as_value=True
71
+ ).resolve()
72
+ return resolved_values | {
73
+ "account_id": account_id,
74
+ }
75
+
76
+ def validate(
77
+ self,
78
+ resource: ExternalResource,
79
+ module_conf: ExternalResourceModuleConfiguration,
80
+ ) -> None: ...
@@ -11,6 +11,10 @@ from reconcile.external_resources.aws import (
11
11
  AWSRdsFactory,
12
12
  AWSResourceFactory,
13
13
  )
14
+ from reconcile.external_resources.cloudflare import (
15
+ CloudflareDefaultResourceFactory,
16
+ CloudflareResourceFactory,
17
+ )
14
18
  from reconcile.external_resources.meta import QONTRACT_INTEGRATION
15
19
  from reconcile.external_resources.model import (
16
20
  ExternalResource,
@@ -171,3 +175,67 @@ class AWSExternalResourceFactory(ExternalResourceFactory):
171
175
  ) -> set[ExternalResourceKey]:
172
176
  f = self.resource_factories.get_factory(spec.provider)
173
177
  return f.find_linked_resources(spec)
178
+
179
+
180
+ def setup_cloudflare_resource_factories(
181
+ er_inventory: ExternalResourcesInventory, secret_reader: SecretReaderBase
182
+ ) -> ObjectFactory[CloudflareResourceFactory]:
183
+ return ObjectFactory[CloudflareResourceFactory](
184
+ factories={},
185
+ default_factory=CloudflareDefaultResourceFactory(er_inventory, secret_reader),
186
+ )
187
+
188
+
189
+ class CloudflareExternalResourceFactory(ExternalResourceFactory):
190
+ def __init__(
191
+ self,
192
+ module_inventory: ModuleInventory,
193
+ er_inventory: ExternalResourcesInventory,
194
+ secret_reader: SecretReaderBase,
195
+ provision_factories: ObjectFactory[ModuleProvisionDataFactory],
196
+ resource_factories: ObjectFactory[CloudflareResourceFactory],
197
+ ):
198
+ self.provision_factories = provision_factories
199
+ self.resource_factories = resource_factories
200
+ self.module_inventory = module_inventory
201
+ self.er_inventory = er_inventory
202
+ self.secret_reader = secret_reader
203
+
204
+ def create_external_resource(
205
+ self,
206
+ spec: ExternalResourceSpec,
207
+ module_conf: ExternalResourceModuleConfiguration,
208
+ ) -> ExternalResource:
209
+ f = self.resource_factories.get_factory(spec.provider)
210
+ data = f.resolve(spec, module_conf)
211
+
212
+ module_type = self.module_inventory.get_from_spec(spec).module_type
213
+ provision_factory = self.provision_factories.get_factory(module_type)
214
+ module_provision_data = provision_factory.create_provision_data(spec)
215
+
216
+ provision = ExternalResourceProvision(
217
+ provision_provider=spec.provision_provider,
218
+ provisioner=spec.provisioner_name,
219
+ provider=spec.provider,
220
+ identifier=spec.identifier,
221
+ target_cluster=spec.cluster_name,
222
+ target_namespace=spec.namespace_name,
223
+ target_secret_name=spec.output_resource_name,
224
+ module_provision_data=module_provision_data,
225
+ )
226
+
227
+ return ExternalResource(data=data, provision=provision)
228
+
229
+ def validate_external_resource(
230
+ self,
231
+ resource: ExternalResource,
232
+ module_conf: ExternalResourceModuleConfiguration,
233
+ ) -> None:
234
+ f = self.resource_factories.get_factory(resource.provision.provider)
235
+ f.validate(resource, module_conf)
236
+
237
+ def find_linked_resources(
238
+ self, spec: ExternalResourceSpec
239
+ ) -> set[ExternalResourceKey]:
240
+ f = self.resource_factories.get_factory(spec.provider)
241
+ return f.find_linked_resources(spec)
@@ -7,11 +7,13 @@ from sretoolbox.utils import threaded
7
7
 
8
8
  from reconcile.external_resources.factories import (
9
9
  AWSExternalResourceFactory,
10
+ CloudflareExternalResourceFactory,
10
11
  ExternalResourceFactory,
11
12
  ModuleProvisionDataFactory,
12
13
  ObjectFactory,
13
14
  TerraformModuleProvisionDataFactory,
14
15
  setup_aws_resource_factories,
16
+ setup_cloudflare_resource_factories,
15
17
  )
16
18
  from reconcile.external_resources.metrics import publish_metrics
17
19
  from reconcile.external_resources.model import (
@@ -55,6 +57,9 @@ def setup_factories(
55
57
  secret_reader: SecretReaderBase,
56
58
  ) -> ObjectFactory[ExternalResourceFactory]:
57
59
  tf_factory = TerraformModuleProvisionDataFactory(settings=settings)
60
+ provision_factories = ObjectFactory[ModuleProvisionDataFactory](
61
+ factories={"terraform": tf_factory}
62
+ )
58
63
 
59
64
  return ObjectFactory[ExternalResourceFactory](
60
65
  factories={
@@ -62,14 +67,21 @@ def setup_factories(
62
67
  module_inventory=module_inventory,
63
68
  er_inventory=er_inventory,
64
69
  secret_reader=secret_reader,
65
- provision_factories=ObjectFactory[ModuleProvisionDataFactory](
66
- factories={"terraform": tf_factory, "cdktf": tf_factory}
67
- ),
70
+ provision_factories=provision_factories,
68
71
  resource_factories=setup_aws_resource_factories(
69
72
  er_inventory, secret_reader
70
73
  ),
71
74
  default_tags=cast("dict[str, str]", settings.default_tags),
72
- )
75
+ ),
76
+ "cloudflare": CloudflareExternalResourceFactory(
77
+ module_inventory=module_inventory,
78
+ er_inventory=er_inventory,
79
+ secret_reader=secret_reader,
80
+ provision_factories=provision_factories,
81
+ resource_factories=setup_cloudflare_resource_factories(
82
+ er_inventory, secret_reader
83
+ ),
84
+ ),
73
85
  }
74
86
  )
75
87
 
@@ -15,6 +15,8 @@ from reconcile.gql_definitions.external_resources.external_resources_modules imp
15
15
  )
16
16
  from reconcile.gql_definitions.external_resources.external_resources_namespaces import (
17
17
  NamespaceTerraformProviderResourceAWSV1,
18
+ NamespaceTerraformProviderResourceCloudflareV1,
19
+ NamespaceTerraformResourceCloudflareZoneV1,
18
20
  NamespaceTerraformResourceCloudWatchV1,
19
21
  NamespaceTerraformResourceElastiCacheV1,
20
22
  NamespaceTerraformResourceKMSV1,
@@ -90,7 +92,10 @@ class ExternalResourceKey(BaseModel, frozen=True):
90
92
  return f"{self.provision_provider}/{self.provisioner_name}/{self.provider}/{self.identifier}"
91
93
 
92
94
 
93
- SUPPORTED_RESOURCE_PROVIDERS = NamespaceTerraformProviderResourceAWSV1
95
+ SUPPORTED_RESOURCE_PROVIDERS = (
96
+ NamespaceTerraformProviderResourceAWSV1
97
+ | NamespaceTerraformProviderResourceCloudflareV1
98
+ )
94
99
  SUPPORTED_RESOURCE_TYPES = (
95
100
  NamespaceTerraformResourceRDSV1
96
101
  | NamespaceTerraformResourceMskV1
@@ -98,6 +103,7 @@ SUPPORTED_RESOURCE_TYPES = (
98
103
  | NamespaceTerraformResourceKMSV1
99
104
  | NamespaceTerraformResourceCloudWatchV1
100
105
  | NamespaceTerraformResourceRDSProxyV1
106
+ | NamespaceTerraformResourceCloudflareZoneV1
101
107
  )
102
108
 
103
109
 
@@ -138,7 +144,8 @@ class ExternalResourcesInventory(MutableMapping):
138
144
  FLAG_RESOURCE_MANAGED_BY_ERV2,
139
145
  FLAG_DELETE_RESOURCE,
140
146
  MODULE_OVERRIDES,
141
- }
147
+ },
148
+ by_alias=True,
142
149
  ),
143
150
  namespace=namespace.model_dump(by_alias=True),
144
151
  )
@@ -406,7 +413,7 @@ class ModuleProvisionData(BaseModel):
406
413
 
407
414
 
408
415
  class TerraformModuleProvisionData(ModuleProvisionData):
409
- """Specific Provision Options for modules based on Terraform or CDKTF"""
416
+ """Specific Provision Options for modules based on Terraform"""
410
417
 
411
418
  tf_state_bucket: str
412
419
  tf_state_region: str
@@ -6,6 +6,7 @@ from typing import Any
6
6
  from kubernetes.client import (
7
7
  V1Container,
8
8
  V1EmptyDirVolumeSource,
9
+ V1EnvFromSource,
9
10
  V1EnvVar,
10
11
  V1EnvVarSource,
11
12
  V1JobSpec,
@@ -15,6 +16,7 @@ from kubernetes.client import (
15
16
  V1PodSpec,
16
17
  V1PodTemplateSpec,
17
18
  V1ResourceRequirements,
19
+ V1SecretEnvSource,
18
20
  V1SecretVolumeSource,
19
21
  V1Volume,
20
22
  V1VolumeMount,
@@ -116,6 +118,14 @@ class ReconciliationK8sJob(K8sJob, BaseModel, frozen=True):
116
118
  value=self.reconciliation.action.value,
117
119
  ),
118
120
  ],
121
+ env_from=[
122
+ V1EnvFromSource(
123
+ secret_ref=V1SecretEnvSource(
124
+ name=f"dotenv-{self.reconciliation.key.provision_provider}-{self.reconciliation.key.provisioner_name}",
125
+ optional=True,
126
+ )
127
+ ),
128
+ ],
119
129
  volume_mounts=[
120
130
  V1VolumeMount(
121
131
  name="credentials",
@@ -192,7 +202,7 @@ class ReconciliationK8sJob(K8sJob, BaseModel, frozen=True):
192
202
  V1Volume(
193
203
  name="credentials",
194
204
  secret=V1SecretVolumeSource(
195
- secret_name=f"credentials-{self.reconciliation.key.provisioner_name}",
205
+ secret_name=f"credentials-{self.reconciliation.key.provision_provider}-{self.reconciliation.key.provisioner_name}",
196
206
  ),
197
207
  ),
198
208
  V1Volume(
@@ -570,6 +570,26 @@ query ExternalResourcesNamespaces {
570
570
  }
571
571
  }
572
572
  }
573
+ ... on NamespaceTerraformProviderResourceCloudflare_v1 {
574
+ provisioner {
575
+ name
576
+ apiCredentials {
577
+ ...VaultSecret
578
+ }
579
+ }
580
+ resources {
581
+ ... on NamespaceTerraformResourceCloudflareZone_v1 {
582
+ provider
583
+ identifier
584
+ defaults
585
+ delete
586
+ managed_by_erv2
587
+ module_overrides {
588
+ ...ExternalResourcesModuleOverrides
589
+ }
590
+ }
591
+ }
592
+ }
573
593
  }
574
594
  environment {
575
595
  name
@@ -1173,6 +1193,29 @@ class NamespaceTerraformProviderResourceAWSV1(NamespaceExternalResourceV1):
1173
1193
  resources: list[Union[NamespaceTerraformResourceRDSV1, NamespaceTerraformResourceRosaAuthenticatorV1, NamespaceTerraformResourceALBV1, NamespaceTerraformResourceS3V1, NamespaceTerraformResourceElastiCacheV1, NamespaceTerraformResourceCloudWatchV1, NamespaceTerraformResourceASGV1, NamespaceTerraformResourceRDSProxyV1, NamespaceTerraformResourceRoleV1, NamespaceTerraformResourceKMSV1, NamespaceTerraformResourceMskV1, NamespaceTerraformResourceSNSTopicV1, NamespaceTerraformResourceServiceAccountV1, NamespaceTerraformResourceS3SQSV1, NamespaceTerraformResourceKinesisV1, NamespaceTerraformResourceRosaAuthenticatorVPCEV1, NamespaceTerraformResourceS3CloudFrontV1, NamespaceTerraformResourceElasticSearchV1, NamespaceTerraformResourceACMV1, NamespaceTerraformResourceRoute53ZoneV1, NamespaceTerraformResourceSQSV1, NamespaceTerraformResourceDynamoDBV1, NamespaceTerraformResourceECRV1, NamespaceTerraformResourceS3CloudFrontPublicKeyV1, NamespaceTerraformResourceSecretsManagerV1, NamespaceTerraformResourceSecretsManagerServiceAccountV1, NamespaceTerraformResourceAWSV1]] = Field(..., alias="resources")
1174
1194
 
1175
1195
 
1196
+ class CloudflareAccountV1(ConfiguredBaseModel):
1197
+ name: str = Field(..., alias="name")
1198
+ api_credentials: VaultSecret = Field(..., alias="apiCredentials")
1199
+
1200
+
1201
+ class NamespaceTerraformResourceCloudflareV1(ConfiguredBaseModel):
1202
+ ...
1203
+
1204
+
1205
+ class NamespaceTerraformResourceCloudflareZoneV1(NamespaceTerraformResourceCloudflareV1):
1206
+ provider: str = Field(..., alias="provider")
1207
+ identifier: str = Field(..., alias="identifier")
1208
+ defaults: str = Field(..., alias="defaults")
1209
+ delete: Optional[bool] = Field(..., alias="delete")
1210
+ managed_by_erv2: Optional[bool] = Field(..., alias="managed_by_erv2")
1211
+ module_overrides: Optional[ExternalResourcesModuleOverrides] = Field(..., alias="module_overrides")
1212
+
1213
+
1214
+ class NamespaceTerraformProviderResourceCloudflareV1(NamespaceExternalResourceV1):
1215
+ provisioner: CloudflareAccountV1 = Field(..., alias="provisioner")
1216
+ resources: list[Union[NamespaceTerraformResourceCloudflareZoneV1, NamespaceTerraformResourceCloudflareV1]] = Field(..., alias="resources")
1217
+
1218
+
1176
1219
  class EnvironmentV1(ConfiguredBaseModel):
1177
1220
  name: str = Field(..., alias="name")
1178
1221
  labels: str = Field(..., alias="labels")
@@ -1213,7 +1256,7 @@ class NamespaceV1(ConfiguredBaseModel):
1213
1256
  delete: Optional[bool] = Field(..., alias="delete")
1214
1257
  cluster_admin: Optional[bool] = Field(..., alias="clusterAdmin")
1215
1258
  managed_external_resources: Optional[bool] = Field(..., alias="managedExternalResources")
1216
- external_resources: Optional[list[Union[NamespaceTerraformProviderResourceAWSV1, NamespaceExternalResourceV1]]] = Field(..., alias="externalResources")
1259
+ external_resources: Optional[list[Union[NamespaceTerraformProviderResourceAWSV1, NamespaceTerraformProviderResourceCloudflareV1, NamespaceExternalResourceV1]]] = Field(..., alias="externalResources")
1217
1260
  environment: EnvironmentV1 = Field(..., alias="environment")
1218
1261
  app: AppV1 = Field(..., alias="app")
1219
1262
  cluster: NamespaceV1_ClusterV1 = Field(..., alias="cluster")
@@ -42711,6 +42711,22 @@
42711
42711
  "isDeprecated": false,
42712
42712
  "deprecationReason": null
42713
42713
  },
42714
+ {
42715
+ "name": "defaults",
42716
+ "description": null,
42717
+ "args": [],
42718
+ "type": {
42719
+ "kind": "NON_NULL",
42720
+ "name": null,
42721
+ "ofType": {
42722
+ "kind": "SCALAR",
42723
+ "name": "String",
42724
+ "ofType": null
42725
+ }
42726
+ },
42727
+ "isDeprecated": false,
42728
+ "deprecationReason": null
42729
+ },
42714
42730
  {
42715
42731
  "name": "zone",
42716
42732
  "description": null,
@@ -42727,6 +42743,42 @@
42727
42743
  "isDeprecated": false,
42728
42744
  "deprecationReason": null
42729
42745
  },
42746
+ {
42747
+ "name": "delete",
42748
+ "description": null,
42749
+ "args": [],
42750
+ "type": {
42751
+ "kind": "SCALAR",
42752
+ "name": "Boolean",
42753
+ "ofType": null
42754
+ },
42755
+ "isDeprecated": false,
42756
+ "deprecationReason": null
42757
+ },
42758
+ {
42759
+ "name": "managed_by_erv2",
42760
+ "description": null,
42761
+ "args": [],
42762
+ "type": {
42763
+ "kind": "SCALAR",
42764
+ "name": "Boolean",
42765
+ "ofType": null
42766
+ },
42767
+ "isDeprecated": false,
42768
+ "deprecationReason": null
42769
+ },
42770
+ {
42771
+ "name": "module_overrides",
42772
+ "description": null,
42773
+ "args": [],
42774
+ "type": {
42775
+ "kind": "OBJECT",
42776
+ "name": "ExternalResourcesModuleOverrides_v1",
42777
+ "ofType": null
42778
+ },
42779
+ "isDeprecated": false,
42780
+ "deprecationReason": null
42781
+ },
42730
42782
  {
42731
42783
  "name": "plan",
42732
42784
  "description": null,
@@ -42800,7 +42852,7 @@
42800
42852
  "deprecationReason": null
42801
42853
  },
42802
42854
  {
42803
- "name": "records",
42855
+ "name": "dns_records",
42804
42856
  "description": null,
42805
42857
  "args": [],
42806
42858
  "type": {
@@ -42891,6 +42943,113 @@
42891
42943
  "enumValues": null,
42892
42944
  "possibleTypes": null
42893
42945
  },
42946
+ {
42947
+ "kind": "OBJECT",
42948
+ "name": "ExternalResourcesModuleOverrides_v1",
42949
+ "description": null,
42950
+ "fields": [
42951
+ {
42952
+ "name": "module_type",
42953
+ "description": null,
42954
+ "args": [],
42955
+ "type": {
42956
+ "kind": "SCALAR",
42957
+ "name": "String",
42958
+ "ofType": null
42959
+ },
42960
+ "isDeprecated": false,
42961
+ "deprecationReason": null
42962
+ },
42963
+ {
42964
+ "name": "channel",
42965
+ "description": null,
42966
+ "args": [],
42967
+ "type": {
42968
+ "kind": "SCALAR",
42969
+ "name": "String",
42970
+ "ofType": null
42971
+ },
42972
+ "isDeprecated": false,
42973
+ "deprecationReason": null
42974
+ },
42975
+ {
42976
+ "name": "image",
42977
+ "description": null,
42978
+ "args": [],
42979
+ "type": {
42980
+ "kind": "SCALAR",
42981
+ "name": "String",
42982
+ "ofType": null
42983
+ },
42984
+ "isDeprecated": false,
42985
+ "deprecationReason": null
42986
+ },
42987
+ {
42988
+ "name": "version",
42989
+ "description": null,
42990
+ "args": [],
42991
+ "type": {
42992
+ "kind": "SCALAR",
42993
+ "name": "String",
42994
+ "ofType": null
42995
+ },
42996
+ "isDeprecated": false,
42997
+ "deprecationReason": null
42998
+ },
42999
+ {
43000
+ "name": "reconcile_timeout_minutes",
43001
+ "description": null,
43002
+ "args": [],
43003
+ "type": {
43004
+ "kind": "SCALAR",
43005
+ "name": "Int",
43006
+ "ofType": null
43007
+ },
43008
+ "isDeprecated": false,
43009
+ "deprecationReason": null
43010
+ },
43011
+ {
43012
+ "name": "outputs_secret_image",
43013
+ "description": null,
43014
+ "args": [],
43015
+ "type": {
43016
+ "kind": "SCALAR",
43017
+ "name": "String",
43018
+ "ofType": null
43019
+ },
43020
+ "isDeprecated": false,
43021
+ "deprecationReason": null
43022
+ },
43023
+ {
43024
+ "name": "outputs_secret_version",
43025
+ "description": null,
43026
+ "args": [],
43027
+ "type": {
43028
+ "kind": "SCALAR",
43029
+ "name": "String",
43030
+ "ofType": null
43031
+ },
43032
+ "isDeprecated": false,
43033
+ "deprecationReason": null
43034
+ },
43035
+ {
43036
+ "name": "resources",
43037
+ "description": null,
43038
+ "args": [],
43039
+ "type": {
43040
+ "kind": "OBJECT",
43041
+ "name": "DeployResources_v1",
43042
+ "ofType": null
43043
+ },
43044
+ "isDeprecated": false,
43045
+ "deprecationReason": null
43046
+ }
43047
+ ],
43048
+ "inputFields": null,
43049
+ "interfaces": [],
43050
+ "enumValues": null,
43051
+ "possibleTypes": null
43052
+ },
42894
43053
  {
42895
43054
  "kind": "OBJECT",
42896
43055
  "name": "CloudflareZoneArgo_v1",
@@ -45058,113 +45217,6 @@
45058
45217
  "enumValues": null,
45059
45218
  "possibleTypes": null
45060
45219
  },
45061
- {
45062
- "kind": "OBJECT",
45063
- "name": "ExternalResourcesModuleOverrides_v1",
45064
- "description": null,
45065
- "fields": [
45066
- {
45067
- "name": "module_type",
45068
- "description": null,
45069
- "args": [],
45070
- "type": {
45071
- "kind": "SCALAR",
45072
- "name": "String",
45073
- "ofType": null
45074
- },
45075
- "isDeprecated": false,
45076
- "deprecationReason": null
45077
- },
45078
- {
45079
- "name": "channel",
45080
- "description": null,
45081
- "args": [],
45082
- "type": {
45083
- "kind": "SCALAR",
45084
- "name": "String",
45085
- "ofType": null
45086
- },
45087
- "isDeprecated": false,
45088
- "deprecationReason": null
45089
- },
45090
- {
45091
- "name": "image",
45092
- "description": null,
45093
- "args": [],
45094
- "type": {
45095
- "kind": "SCALAR",
45096
- "name": "String",
45097
- "ofType": null
45098
- },
45099
- "isDeprecated": false,
45100
- "deprecationReason": null
45101
- },
45102
- {
45103
- "name": "version",
45104
- "description": null,
45105
- "args": [],
45106
- "type": {
45107
- "kind": "SCALAR",
45108
- "name": "String",
45109
- "ofType": null
45110
- },
45111
- "isDeprecated": false,
45112
- "deprecationReason": null
45113
- },
45114
- {
45115
- "name": "reconcile_timeout_minutes",
45116
- "description": null,
45117
- "args": [],
45118
- "type": {
45119
- "kind": "SCALAR",
45120
- "name": "Int",
45121
- "ofType": null
45122
- },
45123
- "isDeprecated": false,
45124
- "deprecationReason": null
45125
- },
45126
- {
45127
- "name": "outputs_secret_image",
45128
- "description": null,
45129
- "args": [],
45130
- "type": {
45131
- "kind": "SCALAR",
45132
- "name": "String",
45133
- "ofType": null
45134
- },
45135
- "isDeprecated": false,
45136
- "deprecationReason": null
45137
- },
45138
- {
45139
- "name": "outputs_secret_version",
45140
- "description": null,
45141
- "args": [],
45142
- "type": {
45143
- "kind": "SCALAR",
45144
- "name": "String",
45145
- "ofType": null
45146
- },
45147
- "isDeprecated": false,
45148
- "deprecationReason": null
45149
- },
45150
- {
45151
- "name": "resources",
45152
- "description": null,
45153
- "args": [],
45154
- "type": {
45155
- "kind": "OBJECT",
45156
- "name": "DeployResources_v1",
45157
- "ofType": null
45158
- },
45159
- "isDeprecated": false,
45160
- "deprecationReason": null
45161
- }
45162
- ],
45163
- "inputFields": null,
45164
- "interfaces": [],
45165
- "enumValues": null,
45166
- "possibleTypes": null
45167
- },
45168
45220
  {
45169
45221
  "kind": "OBJECT",
45170
45222
  "name": "NamespaceTerraformResourceRDS_v1",
@@ -104,7 +104,7 @@ query TerraformCloudflareResources {
104
104
  cache_reserve {
105
105
  enabled
106
106
  }
107
- records {
107
+ records: dns_records {
108
108
  identifier
109
109
  name
110
110
  type
reconcile/utils/vault.py CHANGED
@@ -185,7 +185,10 @@ class VaultClient:
185
185
  mount_point=self.kube_auth_mount,
186
186
  )
187
187
  except Exception as e:
188
- LOG.error(e)
188
+ LOG.error(
189
+ f"Failed to authenticate to Vault server {self._client.url} "
190
+ f"using role '{self.kube_auth_role}' on mount '{self.kube_auth_mount}': {e}"
191
+ )
189
192
  else:
190
193
  self._client.auth_approle(self.role_id, self.secret_id)
191
194
 
@@ -170,62 +170,6 @@ class Erv2Cli:
170
170
  else:
171
171
  rich_print("[b red]External Resource does not exist")
172
172
 
173
- def build_cdktf(self, credentials: Path) -> None:
174
- """Run the CDKTF container and return the generated CDKTF json."""
175
- input_file = self.temp / "input.json"
176
- input_file.write_text(self.input_data)
177
-
178
- # delete previous ERv2 container
179
- run(["docker", "rm", "-f", "erv2"], capture_output=True, check=True)
180
-
181
- try:
182
- cdktf_outdir = "/tmp/cdktf.out"
183
-
184
- # run cdktf synth
185
- with task(self.progress_spinner, "-- Running CDKTF synth"):
186
- run(["docker", "pull", self.image], check=True, capture_output=True)
187
- run(
188
- [
189
- "docker",
190
- "run",
191
- "--name",
192
- "erv2",
193
- "-v",
194
- f"{input_file!s}:/inputs/input.json:Z",
195
- "-v",
196
- f"{credentials!s}:/credentials:Z",
197
- "-e",
198
- "AWS_SHARED_CREDENTIALS_FILE=/credentials",
199
- "--entrypoint",
200
- "cdktf",
201
- self.image,
202
- "synth",
203
- "--output",
204
- cdktf_outdir,
205
- ],
206
- check=True,
207
- capture_output=True,
208
- )
209
-
210
- # # get the cdk.tf.json
211
- with task(self.progress_spinner, "-- Copying the generated cdk.tf.json"):
212
- run(
213
- [
214
- "docker",
215
- "cp",
216
- f"erv2:{cdktf_outdir}/stacks/CDKTF/cdk.tf.json",
217
- str(self.temp),
218
- ],
219
- check=True,
220
- capture_output=True,
221
- )
222
- except CalledProcessError as e:
223
- if e.stderr:
224
- print(e.stderr.decode("utf-8"))
225
- if e.stdout:
226
- print(e.stdout.decode("utf-8"))
227
- raise
228
-
229
173
  def build_terraform(self, credentials: Path) -> None:
230
174
  input_file = self.temp / "input.json"
231
175
  input_file.write_text(self.input_data)
@@ -323,7 +267,7 @@ class Erv2Cli:
323
267
  raise
324
268
 
325
269
  def force_unlock(self, credentials: Path, lock_id: str) -> None:
326
- """Run 'terraform force-unlock' in a CDKTF container."""
270
+ """Run 'terraform force-unlock' in a terraform container."""
327
271
  input_file = self.temp / "input.json"
328
272
  input_file.write_text(self.input_data)
329
273
 
tools/qontract_cli.py CHANGED
@@ -4430,7 +4430,7 @@ def request_reconciliation(ctx: click.Context) -> None:
4430
4430
  )
4431
4431
  @click.option(
4432
4432
  "--skip-build/--no-skip-build",
4433
- help="Skip/Do not skip the terraform and CDKTF builds. Default: build everything!",
4433
+ help="Skip/Do not skip the terraform builds. Default: build everything!",
4434
4434
  default=False,
4435
4435
  )
4436
4436
  @click.pass_context
@@ -4463,8 +4463,8 @@ def migrate(ctx: click.Context, dry_run: bool, skip_build: bool) -> None:
4463
4463
  temp_tfr.mkdir(exist_ok=True)
4464
4464
 
4465
4465
  with progress_spinner() as progress:
4466
- with task(progress, "Preparing AWS credentials for CDKTF and local terraform"):
4467
- # prepare AWS credentials for CDKTF and local terraform
4466
+ with task(progress, "Preparing AWS credentials for local terraform"):
4467
+ # prepare AWS credentials for local terraform
4468
4468
  credentials_file = tempdir / "credentials"
4469
4469
  credentials_file.write_text(
4470
4470
  _get_external_resources_credentials(
@@ -4486,10 +4486,10 @@ def migrate(ctx: click.Context, dry_run: bool, skip_build: bool) -> None:
4486
4486
 
4487
4487
  with task(progress, "(erv2) Building the terraform configuration"):
4488
4488
  if not skip_build:
4489
- if erv2cli.module_type == "cdktf":
4490
- erv2cli.build_cdktf(credentials_file)
4491
- else:
4489
+ if erv2cli.module_type == "terraform":
4492
4490
  erv2cli.build_terraform(credentials_file)
4491
+ else:
4492
+ raise ValueError(f"Unsupported module type: {erv2cli.module_type}")
4493
4493
  erv2_tf_cli = TerraformCli(
4494
4494
  temp_erv2, dry_run=dry_run, progress_spinner=progress
4495
4495
  )