qontract-reconcile 0.10.2.dev503__py3-none-any.whl → 0.10.2.dev505__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.2.dev503.dist-info → qontract_reconcile-0.10.2.dev505.dist-info}/METADATA +1 -4
- {qontract_reconcile-0.10.2.dev503.dist-info → qontract_reconcile-0.10.2.dev505.dist-info}/RECORD +16 -34
- reconcile/cli.py +0 -108
- reconcile/gql_definitions/common/saasherder_settings.py +10 -0
- reconcile/gql_definitions/integrations/integrations.py +1 -31
- reconcile/gql_definitions/introspection.json +0 -220
- reconcile/integrations_manager.py +0 -2
- reconcile/openshift_saas_deploy.py +8 -0
- reconcile/utils/external_resource_spec.py +1 -2
- reconcile/utils/runtime/sharding.py +0 -80
- reconcile/utils/saasherder/interfaces.py +1 -0
- reconcile/utils/saasherder/models.py +8 -0
- reconcile/utils/saasherder/saasherder.py +79 -1
- tools/cli_commands/systems_and_tools.py +0 -23
- reconcile/gql_definitions/terraform_cloudflare_dns/__init__.py +0 -0
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +0 -62
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +0 -193
- reconcile/gql_definitions/terraform_cloudflare_resources/__init__.py +0 -0
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +0 -127
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +0 -359
- reconcile/gql_definitions/terraform_cloudflare_users/__init__.py +0 -0
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +0 -62
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +0 -139
- reconcile/terraform_cloudflare_dns.py +0 -379
- reconcile/terraform_cloudflare_resources.py +0 -445
- reconcile/terraform_cloudflare_users.py +0 -374
- reconcile/typed_queries/cloudflare.py +0 -10
- reconcile/utils/terrascript/__init__.py +0 -0
- reconcile/utils/terrascript/cloudflare_client.py +0 -310
- reconcile/utils/terrascript/cloudflare_resources.py +0 -432
- reconcile/utils/terrascript/models.py +0 -26
- reconcile/utils/terrascript/resources.py +0 -43
- {qontract_reconcile-0.10.2.dev503.dist-info → qontract_reconcile-0.10.2.dev505.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev503.dist-info → qontract_reconcile-0.10.2.dev505.dist-info}/entry_points.txt +0 -0
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import sys
|
|
3
|
-
from collections.abc import Callable, Iterable
|
|
4
|
-
from typing import (
|
|
5
|
-
Any,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
from sretoolbox.utils import threaded
|
|
9
|
-
|
|
10
|
-
from reconcile.gql_definitions.terraform_cloudflare_resources import (
|
|
11
|
-
terraform_cloudflare_accounts,
|
|
12
|
-
terraform_cloudflare_resources,
|
|
13
|
-
)
|
|
14
|
-
from reconcile.gql_definitions.terraform_cloudflare_resources.terraform_cloudflare_accounts import (
|
|
15
|
-
AWSAccountV1,
|
|
16
|
-
CloudflareAccountV1,
|
|
17
|
-
TerraformCloudflareAccountsQueryData,
|
|
18
|
-
)
|
|
19
|
-
from reconcile.gql_definitions.terraform_cloudflare_resources.terraform_cloudflare_resources import (
|
|
20
|
-
NamespaceTerraformProviderResourceCloudflareV1,
|
|
21
|
-
NamespaceV1,
|
|
22
|
-
TerraformCloudflareResourcesQueryData,
|
|
23
|
-
)
|
|
24
|
-
from reconcile.openshift_base import (
|
|
25
|
-
CurrentStateSpec,
|
|
26
|
-
init_specs_to_fetch,
|
|
27
|
-
realize_data,
|
|
28
|
-
)
|
|
29
|
-
from reconcile.status import ExitCodes
|
|
30
|
-
from reconcile.typed_queries.app_interface_vault_settings import (
|
|
31
|
-
get_app_interface_vault_settings,
|
|
32
|
-
)
|
|
33
|
-
from reconcile.utils import gql
|
|
34
|
-
from reconcile.utils.defer import defer
|
|
35
|
-
from reconcile.utils.exceptions import SecretIncompleteError
|
|
36
|
-
from reconcile.utils.external_resource_spec import ExternalResourceSpecInventory
|
|
37
|
-
from reconcile.utils.external_resources import (
|
|
38
|
-
PROVIDER_CLOUDFLARE,
|
|
39
|
-
get_external_resource_specs,
|
|
40
|
-
publish_metrics,
|
|
41
|
-
)
|
|
42
|
-
from reconcile.utils.oc import StatusCodeError
|
|
43
|
-
from reconcile.utils.oc_map import (
|
|
44
|
-
OCMap,
|
|
45
|
-
init_oc_map_from_namespaces,
|
|
46
|
-
)
|
|
47
|
-
from reconcile.utils.openshift_resource import (
|
|
48
|
-
OpenshiftResource,
|
|
49
|
-
ResourceInventory,
|
|
50
|
-
)
|
|
51
|
-
from reconcile.utils.secret_reader import (
|
|
52
|
-
SecretReaderBase,
|
|
53
|
-
create_secret_reader,
|
|
54
|
-
)
|
|
55
|
-
from reconcile.utils.semver_helper import make_semver
|
|
56
|
-
from reconcile.utils.terraform.config_client import TerraformConfigClientCollection
|
|
57
|
-
from reconcile.utils.terraform_client import TerraformClient
|
|
58
|
-
from reconcile.utils.terrascript.cloudflare_client import (
|
|
59
|
-
DEFAULT_CLOUDFLARE_ACCOUNT_2FA,
|
|
60
|
-
DEFAULT_CLOUDFLARE_ACCOUNT_TYPE,
|
|
61
|
-
CloudflareAccountConfig,
|
|
62
|
-
TerraformS3BackendConfig,
|
|
63
|
-
TerrascriptCloudflareClient,
|
|
64
|
-
create_cloudflare_terrascript,
|
|
65
|
-
)
|
|
66
|
-
from reconcile.utils.vault import (
|
|
67
|
-
VaultClient,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
QONTRACT_INTEGRATION = "terraform_cloudflare_resources"
|
|
71
|
-
QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
|
72
|
-
QONTRACT_TF_PREFIX = "qrtfcf"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def create_backend_config(
|
|
76
|
-
secret_reader: SecretReaderBase,
|
|
77
|
-
aws_acct: AWSAccountV1,
|
|
78
|
-
cf_acct: CloudflareAccountV1,
|
|
79
|
-
) -> TerraformS3BackendConfig:
|
|
80
|
-
aws_acct_creds = secret_reader.read_all_secret(aws_acct.automation_token)
|
|
81
|
-
|
|
82
|
-
# default from AWS account file
|
|
83
|
-
tf_state = aws_acct.terraform_state
|
|
84
|
-
if tf_state is None:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
f"AWS account {aws_acct.name} cannot be used for Cloudflare "
|
|
87
|
-
f"account {cf_acct.name} because it does define a terraform state "
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
integrations = tf_state.integrations or []
|
|
91
|
-
bucket_key = bucket_name = bucket_region = None
|
|
92
|
-
for i in integrations or []:
|
|
93
|
-
name = i.integration
|
|
94
|
-
if name.replace("-", "_") == QONTRACT_INTEGRATION:
|
|
95
|
-
# we have to ensure the bucket key(file) is unique across
|
|
96
|
-
# Cloudflare accounts to support running per-account
|
|
97
|
-
bucket_key = f"{QONTRACT_INTEGRATION}-{cf_acct.name}.tfstate"
|
|
98
|
-
bucket_name = tf_state.bucket
|
|
99
|
-
bucket_region = tf_state.region
|
|
100
|
-
break
|
|
101
|
-
|
|
102
|
-
if bucket_name and bucket_key and bucket_region:
|
|
103
|
-
backend_config = TerraformS3BackendConfig(
|
|
104
|
-
aws_acct_creds["aws_access_key_id"],
|
|
105
|
-
aws_acct_creds["aws_secret_access_key"],
|
|
106
|
-
bucket_name,
|
|
107
|
-
bucket_key,
|
|
108
|
-
bucket_region,
|
|
109
|
-
)
|
|
110
|
-
else:
|
|
111
|
-
raise ValueError(f"No state bucket config found for account {aws_acct.name}")
|
|
112
|
-
|
|
113
|
-
return backend_config
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def build_clients(
|
|
117
|
-
secret_reader: SecretReaderBase,
|
|
118
|
-
query_accounts: TerraformCloudflareAccountsQueryData,
|
|
119
|
-
selected_account: str | None = None,
|
|
120
|
-
) -> list[tuple[str, TerrascriptCloudflareClient]]:
|
|
121
|
-
clients = []
|
|
122
|
-
for cf_acct in query_accounts.accounts or []:
|
|
123
|
-
if selected_account and cf_acct.name != selected_account:
|
|
124
|
-
continue
|
|
125
|
-
cf_acct_creds = secret_reader.read_all_secret(cf_acct.api_credentials)
|
|
126
|
-
if not cf_acct_creds.get("api_token") or not cf_acct_creds.get("account_id"):
|
|
127
|
-
raise SecretIncompleteError(
|
|
128
|
-
f"secret {cf_acct.api_credentials.path} incomplete: api_token and/or account_id missing"
|
|
129
|
-
)
|
|
130
|
-
cf_acct_config = CloudflareAccountConfig(
|
|
131
|
-
cf_acct.name,
|
|
132
|
-
cf_acct_creds["api_token"],
|
|
133
|
-
cf_acct_creds["account_id"],
|
|
134
|
-
cf_acct.enforce_twofactor or DEFAULT_CLOUDFLARE_ACCOUNT_2FA,
|
|
135
|
-
cf_acct.q_type or DEFAULT_CLOUDFLARE_ACCOUNT_TYPE,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
aws_acct = cf_acct.terraform_state_account
|
|
139
|
-
aws_backend_config = create_backend_config(secret_reader, aws_acct, cf_acct)
|
|
140
|
-
|
|
141
|
-
ts_config = create_cloudflare_terrascript(
|
|
142
|
-
cf_acct_config,
|
|
143
|
-
aws_backend_config,
|
|
144
|
-
cf_acct.provider_version,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
ts_client = TerrascriptCloudflareClient(ts_config)
|
|
148
|
-
clients.append((cf_acct.name, ts_client))
|
|
149
|
-
return clients
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def _build_oc_resources(
|
|
153
|
-
cloudflare_namespaces: Iterable[NamespaceV1],
|
|
154
|
-
secret_reader: SecretReaderBase,
|
|
155
|
-
use_jump_host: bool,
|
|
156
|
-
thread_pool_size: int,
|
|
157
|
-
internal: bool | None = None,
|
|
158
|
-
account_names: Iterable[str] | None = None,
|
|
159
|
-
) -> tuple[ResourceInventory, OCMap]:
|
|
160
|
-
ri = ResourceInventory()
|
|
161
|
-
|
|
162
|
-
oc_map = init_oc_map_from_namespaces(
|
|
163
|
-
cloudflare_namespaces,
|
|
164
|
-
secret_reader,
|
|
165
|
-
integration=QONTRACT_INTEGRATION,
|
|
166
|
-
use_jump_host=use_jump_host,
|
|
167
|
-
thread_pool_size=thread_pool_size,
|
|
168
|
-
internal=internal,
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
namespace_mapping = [ns.model_dump() for ns in cloudflare_namespaces]
|
|
172
|
-
|
|
173
|
-
state_specs = init_specs_to_fetch(
|
|
174
|
-
ri, oc_map, namespaces=namespace_mapping, override_managed_types=["Secret"]
|
|
175
|
-
)
|
|
176
|
-
current_state_specs: list[CurrentStateSpec] = [
|
|
177
|
-
s for s in state_specs if isinstance(s, CurrentStateSpec)
|
|
178
|
-
]
|
|
179
|
-
threaded.run(
|
|
180
|
-
_populate_oc_resources,
|
|
181
|
-
current_state_specs,
|
|
182
|
-
thread_pool_size,
|
|
183
|
-
ri=ri,
|
|
184
|
-
account_names=account_names,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
return ri, oc_map
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def _populate_oc_resources(
|
|
191
|
-
spec: CurrentStateSpec,
|
|
192
|
-
ri: ResourceInventory,
|
|
193
|
-
account_names: Iterable[str] | None,
|
|
194
|
-
) -> None:
|
|
195
|
-
"""
|
|
196
|
-
This was taken from terraform_resources and might be a later candidate for DRY.
|
|
197
|
-
"""
|
|
198
|
-
if spec.oc is None:
|
|
199
|
-
return
|
|
200
|
-
logging.debug(
|
|
201
|
-
"[populate_oc_resources] cluster: "
|
|
202
|
-
+ spec.cluster
|
|
203
|
-
+ " namespace: "
|
|
204
|
-
+ spec.namespace
|
|
205
|
-
+ " resource: "
|
|
206
|
-
+ spec.kind
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
try:
|
|
210
|
-
for item in spec.oc.get_items(spec.kind, namespace=spec.namespace):
|
|
211
|
-
openshift_resource = OpenshiftResource(
|
|
212
|
-
item, QONTRACT_INTEGRATION, QONTRACT_INTEGRATION_VERSION
|
|
213
|
-
)
|
|
214
|
-
if account_names:
|
|
215
|
-
caller = openshift_resource.caller
|
|
216
|
-
if caller and caller not in account_names:
|
|
217
|
-
continue
|
|
218
|
-
|
|
219
|
-
ri.add_current(
|
|
220
|
-
spec.cluster,
|
|
221
|
-
spec.namespace,
|
|
222
|
-
spec.kind,
|
|
223
|
-
openshift_resource.name,
|
|
224
|
-
openshift_resource,
|
|
225
|
-
)
|
|
226
|
-
except StatusCodeError as e:
|
|
227
|
-
ri.register_error(cluster=spec.cluster)
|
|
228
|
-
msg = "cluster: {},"
|
|
229
|
-
msg += "namespace: {},"
|
|
230
|
-
msg += "resource: {},"
|
|
231
|
-
msg += "exception: {}"
|
|
232
|
-
msg = msg.format(spec.cluster, spec.namespace, spec.kind, str(e))
|
|
233
|
-
logging.error(msg)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _populate_desired_state(
|
|
237
|
-
ri: ResourceInventory, resource_specs: ExternalResourceSpecInventory
|
|
238
|
-
) -> None:
|
|
239
|
-
for spec in resource_specs.values():
|
|
240
|
-
if ri.is_cluster_present(spec.cluster_name):
|
|
241
|
-
oc_resource = spec.build_oc_secret(
|
|
242
|
-
QONTRACT_INTEGRATION, QONTRACT_INTEGRATION_VERSION
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
if not oc_resource.body.get("data"):
|
|
246
|
-
logging.debug(
|
|
247
|
-
"Skipping oc_resource %s because there is no Secret data (not all resources have outputs)",
|
|
248
|
-
oc_resource.name,
|
|
249
|
-
)
|
|
250
|
-
continue
|
|
251
|
-
|
|
252
|
-
ri.add_desired(
|
|
253
|
-
cluster=spec.cluster_name,
|
|
254
|
-
namespace=spec.namespace_name,
|
|
255
|
-
resource_type=oc_resource.kind,
|
|
256
|
-
name=spec.output_resource_name,
|
|
257
|
-
value=oc_resource,
|
|
258
|
-
privileged=spec.namespace.get("clusterAdmin") or False,
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def _write_external_resource_secrets_to_vault(
|
|
263
|
-
vault_path: str,
|
|
264
|
-
resource_specs: ExternalResourceSpecInventory,
|
|
265
|
-
integration_name: str,
|
|
266
|
-
) -> None:
|
|
267
|
-
"""
|
|
268
|
-
Write the secrets associated with an external resource to Vault. This was taken
|
|
269
|
-
from terraform-resources with minor modifications. We can consider moving this to a
|
|
270
|
-
separate module if we have additional needs for a similar function.
|
|
271
|
-
"""
|
|
272
|
-
integration_name = integration_name.replace("_", "-")
|
|
273
|
-
vault_client = VaultClient.get_instance()
|
|
274
|
-
for spec in resource_specs.values():
|
|
275
|
-
# A secret can be empty if the terraform-* integrations are not enabled on the cluster
|
|
276
|
-
# the resource is defined on - lets skip vault writes for those right now and
|
|
277
|
-
# give this more thought - e.g. not processing such specs at all when the integration
|
|
278
|
-
# is disabled
|
|
279
|
-
if spec.secret:
|
|
280
|
-
secret_path = f"{vault_path}/{integration_name}/{spec.cluster_name}/{spec.namespace_name}/{spec.output_resource_name}"
|
|
281
|
-
# vault only stores strings as values - by converting to str upfront, we can compare current to desired
|
|
282
|
-
stringified_secret = {k: str(v) for k, v in spec.secret.items()}
|
|
283
|
-
desired_secret = {"path": secret_path, "data": stringified_secret}
|
|
284
|
-
vault_client.write(desired_secret, decode_base64=False)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def _filter_cloudflare_namespaces(
|
|
288
|
-
namespaces: Iterable[NamespaceV1], account_names: set[str]
|
|
289
|
-
) -> list[NamespaceV1]:
|
|
290
|
-
"""
|
|
291
|
-
Get only the namespaces that have Cloudflare resources and that match account_names.
|
|
292
|
-
"""
|
|
293
|
-
cloudflare_namespaces: list[NamespaceV1] = []
|
|
294
|
-
for ns in namespaces:
|
|
295
|
-
if ns.external_resources:
|
|
296
|
-
for resource in ns.external_resources:
|
|
297
|
-
if isinstance(resource, NamespaceTerraformProviderResourceCloudflareV1):
|
|
298
|
-
if (
|
|
299
|
-
resource.provider == PROVIDER_CLOUDFLARE
|
|
300
|
-
and resource.provisioner.name in account_names
|
|
301
|
-
):
|
|
302
|
-
cloudflare_namespaces.append(ns)
|
|
303
|
-
return cloudflare_namespaces
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
@defer
|
|
307
|
-
def run(
|
|
308
|
-
dry_run: bool,
|
|
309
|
-
print_to_file: str | None,
|
|
310
|
-
enable_deletion: bool,
|
|
311
|
-
thread_pool_size: int,
|
|
312
|
-
selected_account: str | None = None,
|
|
313
|
-
vault_output_path: str = "",
|
|
314
|
-
internal: bool | None = None,
|
|
315
|
-
use_jump_host: bool = True,
|
|
316
|
-
defer: Callable | None = None,
|
|
317
|
-
) -> None:
|
|
318
|
-
vault_settings = get_app_interface_vault_settings()
|
|
319
|
-
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
|
320
|
-
|
|
321
|
-
query_accounts, query_resources = _get_cloudflare_desired_state()
|
|
322
|
-
|
|
323
|
-
if not query_accounts.accounts:
|
|
324
|
-
logging.info("No Cloudflare accounts were detected, nothing to do.")
|
|
325
|
-
sys.exit(ExitCodes.SUCCESS)
|
|
326
|
-
|
|
327
|
-
if not query_resources.namespaces:
|
|
328
|
-
logging.info("No namespaces were detected, nothing to do.")
|
|
329
|
-
sys.exit(ExitCodes.SUCCESS)
|
|
330
|
-
|
|
331
|
-
if selected_account:
|
|
332
|
-
account_names = [selected_account]
|
|
333
|
-
else:
|
|
334
|
-
account_names = [acct.name for acct in query_accounts.accounts]
|
|
335
|
-
|
|
336
|
-
cloudflare_namespaces = _filter_cloudflare_namespaces(
|
|
337
|
-
query_resources.namespaces, set(account_names)
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
if not cloudflare_namespaces:
|
|
341
|
-
logging.debug("No cloudflare namespaces were detected, nothing to do.")
|
|
342
|
-
sys.exit(ExitCodes.SUCCESS)
|
|
343
|
-
|
|
344
|
-
# Build Cloudflare clients
|
|
345
|
-
cf_clients = TerraformConfigClientCollection()
|
|
346
|
-
for client in build_clients(secret_reader, query_accounts, selected_account):
|
|
347
|
-
cf_clients.register_client(*client)
|
|
348
|
-
|
|
349
|
-
# Register Cloudflare resources
|
|
350
|
-
cf_specs = [
|
|
351
|
-
spec
|
|
352
|
-
for namespace in query_resources.namespaces
|
|
353
|
-
for spec in get_external_resource_specs(
|
|
354
|
-
namespace.model_dump(by_alias=True), PROVIDER_CLOUDFLARE
|
|
355
|
-
)
|
|
356
|
-
if not selected_account or spec.provisioner_name == selected_account
|
|
357
|
-
]
|
|
358
|
-
cf_clients.add_specs(cf_specs)
|
|
359
|
-
|
|
360
|
-
cf_clients.populate_resources()
|
|
361
|
-
|
|
362
|
-
publish_metrics(cf_clients.resource_spec_inventory, QONTRACT_INTEGRATION)
|
|
363
|
-
|
|
364
|
-
ri, oc_map = _build_oc_resources(
|
|
365
|
-
cloudflare_namespaces,
|
|
366
|
-
secret_reader,
|
|
367
|
-
use_jump_host=use_jump_host,
|
|
368
|
-
thread_pool_size=thread_pool_size,
|
|
369
|
-
internal=internal,
|
|
370
|
-
account_names=account_names,
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
if defer:
|
|
374
|
-
defer(oc_map.cleanup)
|
|
375
|
-
|
|
376
|
-
working_dirs = cf_clients.dump(print_to_file=print_to_file)
|
|
377
|
-
|
|
378
|
-
if print_to_file:
|
|
379
|
-
sys.exit(ExitCodes.SUCCESS)
|
|
380
|
-
|
|
381
|
-
tf = TerraformClient(
|
|
382
|
-
QONTRACT_INTEGRATION,
|
|
383
|
-
QONTRACT_INTEGRATION_VERSION,
|
|
384
|
-
QONTRACT_TF_PREFIX,
|
|
385
|
-
[
|
|
386
|
-
acct.model_dump(by_alias=True) # convert CloudflareAccountV1 to dict
|
|
387
|
-
for acct in query_accounts.accounts or []
|
|
388
|
-
if acct.name in cf_clients.dump() # use only if it is a registered client
|
|
389
|
-
],
|
|
390
|
-
working_dirs,
|
|
391
|
-
thread_pool_size,
|
|
392
|
-
)
|
|
393
|
-
if defer:
|
|
394
|
-
defer(tf.cleanup)
|
|
395
|
-
|
|
396
|
-
disabled_deletions_detected, err = tf.plan(enable_deletion)
|
|
397
|
-
if err:
|
|
398
|
-
sys.exit(ExitCodes.ERROR)
|
|
399
|
-
if disabled_deletions_detected:
|
|
400
|
-
logging.error("Deletions detected but they are disabled")
|
|
401
|
-
sys.exit(ExitCodes.ERROR)
|
|
402
|
-
|
|
403
|
-
if dry_run:
|
|
404
|
-
sys.exit(ExitCodes.SUCCESS)
|
|
405
|
-
|
|
406
|
-
err = tf.apply()
|
|
407
|
-
if err:
|
|
408
|
-
sys.exit(ExitCodes.ERROR)
|
|
409
|
-
|
|
410
|
-
# refresh output data after terraform apply
|
|
411
|
-
tf.populate_terraform_output_secrets(
|
|
412
|
-
resource_specs=cf_clients.resource_spec_inventory
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
# populate the resource inventory with latest output data
|
|
416
|
-
_populate_desired_state(ri, cf_clients.resource_spec_inventory)
|
|
417
|
-
|
|
418
|
-
actions = realize_data(
|
|
419
|
-
dry_run, oc_map, ri, thread_pool_size, caller=selected_account
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
if actions and vault_output_path:
|
|
423
|
-
_write_external_resource_secrets_to_vault(
|
|
424
|
-
vault_output_path,
|
|
425
|
-
cf_clients.resource_spec_inventory,
|
|
426
|
-
QONTRACT_INTEGRATION.replace("_", "-"),
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
def _get_cloudflare_desired_state() -> tuple[
|
|
431
|
-
TerraformCloudflareAccountsQueryData,
|
|
432
|
-
TerraformCloudflareResourcesQueryData,
|
|
433
|
-
]:
|
|
434
|
-
query_accounts = terraform_cloudflare_accounts.query(query_func=gql.get_api().query)
|
|
435
|
-
query_resources = terraform_cloudflare_resources.query(
|
|
436
|
-
query_func=gql.get_api().query
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
return query_accounts, query_resources
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
|
443
|
-
desired_state = _get_cloudflare_desired_state()
|
|
444
|
-
|
|
445
|
-
return {str(state): state.model_dump() for state in desired_state}
|