dbt-platform-helper 12.0.2__py3-none-any.whl → 12.2.0__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.
Potentially problematic release.
This version of dbt-platform-helper might be problematic. Click here for more details.
- dbt_platform_helper/COMMANDS.md +7 -8
- dbt_platform_helper/commands/application.py +1 -0
- dbt_platform_helper/commands/codebase.py +63 -228
- dbt_platform_helper/commands/conduit.py +34 -409
- dbt_platform_helper/commands/secrets.py +1 -1
- dbt_platform_helper/constants.py +12 -1
- dbt_platform_helper/domain/codebase.py +222 -0
- dbt_platform_helper/domain/conduit.py +172 -0
- dbt_platform_helper/domain/database_copy.py +1 -1
- dbt_platform_helper/exceptions.py +61 -0
- dbt_platform_helper/providers/__init__.py +0 -0
- dbt_platform_helper/providers/cloudformation.py +105 -0
- dbt_platform_helper/providers/copilot.py +144 -0
- dbt_platform_helper/providers/ecs.py +78 -0
- dbt_platform_helper/providers/secrets.py +85 -0
- dbt_platform_helper/templates/addons/svc/prometheus-policy.yml +2 -0
- dbt_platform_helper/templates/pipelines/environments/manifest.yml +0 -1
- dbt_platform_helper/utils/application.py +1 -4
- dbt_platform_helper/utils/aws.py +132 -0
- dbt_platform_helper/utils/files.py +70 -0
- dbt_platform_helper/utils/git.py +13 -0
- dbt_platform_helper/utils/validation.py +121 -3
- {dbt_platform_helper-12.0.2.dist-info → dbt_platform_helper-12.2.0.dist-info}/METADATA +2 -1
- {dbt_platform_helper-12.0.2.dist-info → dbt_platform_helper-12.2.0.dist-info}/RECORD +27 -29
- {dbt_platform_helper-12.0.2.dist-info → dbt_platform_helper-12.2.0.dist-info}/WHEEL +1 -1
- dbt_platform_helper/templates/env/overrides/.gitignore +0 -12
- dbt_platform_helper/templates/env/overrides/README.md +0 -11
- dbt_platform_helper/templates/env/overrides/bin/override.ts +0 -9
- dbt_platform_helper/templates/env/overrides/cdk.json +0 -20
- dbt_platform_helper/templates/env/overrides/log_resource_policy.json +0 -68
- dbt_platform_helper/templates/env/overrides/package-lock.json +0 -4307
- dbt_platform_helper/templates/env/overrides/package.json +0 -27
- dbt_platform_helper/templates/env/overrides/stack.ts +0 -51
- dbt_platform_helper/templates/env/overrides/tsconfig.json +0 -32
- {dbt_platform_helper-12.0.2.dist-info → dbt_platform_helper-12.2.0.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-12.0.2.dist-info → dbt_platform_helper-12.2.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,453 +1,78 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import random
|
|
3
|
-
import string
|
|
4
|
-
import subprocess
|
|
5
|
-
import time
|
|
6
|
-
|
|
7
1
|
import click
|
|
8
|
-
from botocore.exceptions import ClientError
|
|
9
|
-
from cfn_tools import dump_yaml
|
|
10
|
-
from cfn_tools import load_yaml
|
|
11
2
|
|
|
12
|
-
from dbt_platform_helper.
|
|
3
|
+
from dbt_platform_helper.constants import CONDUIT_ADDON_TYPES
|
|
4
|
+
from dbt_platform_helper.domain.conduit import Conduit
|
|
5
|
+
from dbt_platform_helper.exceptions import AddonNotFoundError
|
|
6
|
+
from dbt_platform_helper.exceptions import AddonTypeMissingFromConfigError
|
|
7
|
+
from dbt_platform_helper.exceptions import CreateTaskTimeoutError
|
|
8
|
+
from dbt_platform_helper.exceptions import InvalidAddonTypeError
|
|
9
|
+
from dbt_platform_helper.exceptions import NoClusterError
|
|
10
|
+
from dbt_platform_helper.exceptions import ParameterNotFoundError
|
|
11
|
+
from dbt_platform_helper.providers.secrets import SecretNotFoundError
|
|
13
12
|
from dbt_platform_helper.utils.application import load_application
|
|
14
|
-
from dbt_platform_helper.utils.aws import (
|
|
15
|
-
get_postgres_connection_data_updated_with_master_secret,
|
|
16
|
-
)
|
|
17
13
|
from dbt_platform_helper.utils.click import ClickDocOptCommand
|
|
18
|
-
from dbt_platform_helper.utils.messages import abort_with_error
|
|
19
14
|
from dbt_platform_helper.utils.versioning import (
|
|
20
15
|
check_platform_helper_version_needs_update,
|
|
21
16
|
)
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
class ConduitError(Exception):
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class InvalidAddonTypeConduitError(ConduitError):
|
|
29
|
-
def __init__(self, addon_type):
|
|
30
|
-
self.addon_type = addon_type
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class NoClusterConduitError(ConduitError):
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class SecretNotFoundConduitError(ConduitError):
|
|
38
|
-
pass
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class CreateTaskTimeoutConduitError(ConduitError):
|
|
42
|
-
pass
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class ParameterNotFoundConduitError(ConduitError):
|
|
46
|
-
pass
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class AddonNotFoundConduitError(ConduitError):
|
|
50
|
-
pass
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
CONDUIT_DOCKER_IMAGE_LOCATION = "public.ecr.aws/uktrade/tunnel"
|
|
54
|
-
CONDUIT_ADDON_TYPES = [
|
|
55
|
-
"opensearch",
|
|
56
|
-
"postgres",
|
|
57
|
-
"redis",
|
|
58
|
-
]
|
|
59
18
|
CONDUIT_ACCESS_OPTIONS = ["read", "write", "admin"]
|
|
60
19
|
|
|
61
20
|
|
|
62
|
-
def normalise_secret_name(addon_name: str) -> str:
|
|
63
|
-
return addon_name.replace("-", "_").upper()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def get_addon_type(app: Application, env: str, addon_name: str) -> str:
|
|
67
|
-
session = app.environments[env].session
|
|
68
|
-
ssm_client = session.client("ssm")
|
|
69
|
-
addon_type = None
|
|
70
|
-
|
|
71
|
-
try:
|
|
72
|
-
addon_config = json.loads(
|
|
73
|
-
ssm_client.get_parameter(
|
|
74
|
-
Name=f"/copilot/applications/{app.name}/environments/{env}/addons"
|
|
75
|
-
)["Parameter"]["Value"]
|
|
76
|
-
)
|
|
77
|
-
except ssm_client.exceptions.ParameterNotFound:
|
|
78
|
-
raise ParameterNotFoundConduitError
|
|
79
|
-
|
|
80
|
-
if addon_name not in addon_config.keys():
|
|
81
|
-
raise AddonNotFoundConduitError
|
|
82
|
-
|
|
83
|
-
for name, config in addon_config.items():
|
|
84
|
-
if name == addon_name:
|
|
85
|
-
addon_type = config["type"]
|
|
86
|
-
|
|
87
|
-
if not addon_type or addon_type not in CONDUIT_ADDON_TYPES:
|
|
88
|
-
raise InvalidAddonTypeConduitError(addon_type)
|
|
89
|
-
|
|
90
|
-
if "postgres" in addon_type:
|
|
91
|
-
addon_type = "postgres"
|
|
92
|
-
|
|
93
|
-
return addon_type
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_parameter_name(
|
|
97
|
-
app: Application, env: str, addon_type: str, addon_name: str, access: str
|
|
98
|
-
) -> str:
|
|
99
|
-
if addon_type == "postgres":
|
|
100
|
-
return f"/copilot/{app.name}/{env}/conduits/{normalise_secret_name(addon_name)}_{access.upper()}"
|
|
101
|
-
elif addon_type == "redis" or addon_type == "opensearch":
|
|
102
|
-
return f"/copilot/{app.name}/{env}/conduits/{normalise_secret_name(addon_name)}_ENDPOINT"
|
|
103
|
-
else:
|
|
104
|
-
return f"/copilot/{app.name}/{env}/conduits/{normalise_secret_name(addon_name)}"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def get_or_create_task_name(
|
|
108
|
-
app: Application, env: str, addon_name: str, parameter_name: str
|
|
109
|
-
) -> str:
|
|
110
|
-
ssm = app.environments[env].session.client("ssm")
|
|
111
|
-
|
|
112
|
-
try:
|
|
113
|
-
return ssm.get_parameter(Name=parameter_name)["Parameter"]["Value"]
|
|
114
|
-
except ssm.exceptions.ParameterNotFound:
|
|
115
|
-
random_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=12))
|
|
116
|
-
return f"conduit-{app.name}-{env}-{addon_name}-{random_id}"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def get_cluster_arn(app: Application, env: str) -> str:
|
|
120
|
-
ecs_client = app.environments[env].session.client("ecs")
|
|
121
|
-
|
|
122
|
-
for cluster_arn in ecs_client.list_clusters()["clusterArns"]:
|
|
123
|
-
tags_response = ecs_client.list_tags_for_resource(resourceArn=cluster_arn)
|
|
124
|
-
tags = tags_response["tags"]
|
|
125
|
-
|
|
126
|
-
app_key_found = False
|
|
127
|
-
env_key_found = False
|
|
128
|
-
cluster_key_found = False
|
|
129
|
-
|
|
130
|
-
for tag in tags:
|
|
131
|
-
if tag["key"] == "copilot-application" and tag["value"] == app.name:
|
|
132
|
-
app_key_found = True
|
|
133
|
-
if tag["key"] == "copilot-environment" and tag["value"] == env:
|
|
134
|
-
env_key_found = True
|
|
135
|
-
if tag["key"] == "aws:cloudformation:logical-id" and tag["value"] == "Cluster":
|
|
136
|
-
cluster_key_found = True
|
|
137
|
-
|
|
138
|
-
if app_key_found and env_key_found and cluster_key_found:
|
|
139
|
-
return cluster_arn
|
|
140
|
-
|
|
141
|
-
raise NoClusterConduitError
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def get_connection_secret_arn(app: Application, env: str, secret_name: str) -> str:
|
|
145
|
-
secrets_manager = app.environments[env].session.client("secretsmanager")
|
|
146
|
-
ssm = app.environments[env].session.client("ssm")
|
|
147
|
-
|
|
148
|
-
try:
|
|
149
|
-
return ssm.get_parameter(Name=secret_name, WithDecryption=False)["Parameter"]["ARN"]
|
|
150
|
-
except ssm.exceptions.ParameterNotFound:
|
|
151
|
-
pass
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
return secrets_manager.describe_secret(SecretId=secret_name)["ARN"]
|
|
155
|
-
except secrets_manager.exceptions.ResourceNotFoundException:
|
|
156
|
-
pass
|
|
157
|
-
|
|
158
|
-
raise SecretNotFoundConduitError(secret_name)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def create_postgres_admin_task(
|
|
162
|
-
app: Application, env: str, secret_name: str, task_name: str, addon_type: str, addon_name: str
|
|
163
|
-
):
|
|
164
|
-
session = app.environments[env].session
|
|
165
|
-
read_only_secret_name = secret_name + "_READ_ONLY_USER"
|
|
166
|
-
master_secret_name = (
|
|
167
|
-
f"/copilot/{app.name}/{env}/secrets/{normalise_secret_name(addon_name)}_RDS_MASTER_ARN"
|
|
168
|
-
)
|
|
169
|
-
master_secret_arn = session.client("ssm").get_parameter(
|
|
170
|
-
Name=master_secret_name, WithDecryption=True
|
|
171
|
-
)["Parameter"]["Value"]
|
|
172
|
-
connection_string = json.dumps(
|
|
173
|
-
get_postgres_connection_data_updated_with_master_secret(
|
|
174
|
-
session, read_only_secret_name, master_secret_arn
|
|
175
|
-
)
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
subprocess.call(
|
|
179
|
-
f"copilot task run --app {app.name} --env {env} "
|
|
180
|
-
f"--task-group-name {task_name} "
|
|
181
|
-
f"--image {CONDUIT_DOCKER_IMAGE_LOCATION}:{addon_type} "
|
|
182
|
-
f"--env-vars CONNECTION_SECRET='{connection_string}' "
|
|
183
|
-
"--platform-os linux "
|
|
184
|
-
"--platform-arch arm64",
|
|
185
|
-
shell=True,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def create_addon_client_task(
|
|
190
|
-
app: Application,
|
|
191
|
-
env: str,
|
|
192
|
-
addon_type: str,
|
|
193
|
-
addon_name: str,
|
|
194
|
-
task_name: str,
|
|
195
|
-
access: str,
|
|
196
|
-
):
|
|
197
|
-
secret_name = f"/copilot/{app.name}/{env}/secrets/{normalise_secret_name(addon_name)}"
|
|
198
|
-
session = app.environments[env].session
|
|
199
|
-
|
|
200
|
-
if addon_type == "postgres":
|
|
201
|
-
if access == "read":
|
|
202
|
-
secret_name += "_READ_ONLY_USER"
|
|
203
|
-
elif access == "write":
|
|
204
|
-
secret_name += "_APPLICATION_USER"
|
|
205
|
-
elif access == "admin":
|
|
206
|
-
create_postgres_admin_task(app, env, secret_name, task_name, addon_type, addon_name)
|
|
207
|
-
return
|
|
208
|
-
elif addon_type == "redis" or addon_type == "opensearch":
|
|
209
|
-
secret_name += "_ENDPOINT"
|
|
210
|
-
|
|
211
|
-
role_name = f"{addon_name}-{app.name}-{env}-conduitEcsTask"
|
|
212
|
-
|
|
213
|
-
try:
|
|
214
|
-
session.client("iam").get_role(RoleName=role_name)
|
|
215
|
-
execution_role = f"--execution-role {role_name} "
|
|
216
|
-
except ClientError as ex:
|
|
217
|
-
execution_role = ""
|
|
218
|
-
# We cannot check for botocore.errorfactory.NoSuchEntityException as botocore generates that class on the fly as part of errorfactory.
|
|
219
|
-
# factory. Checking the error code is the recommended way of handling these exceptions.
|
|
220
|
-
if ex.response.get("Error", {}).get("Code", None) != "NoSuchEntity":
|
|
221
|
-
abort_with_error(
|
|
222
|
-
f"cannot obtain Role {role_name}: {ex.response.get('Error', {}).get('Message', '')}"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
subprocess.call(
|
|
226
|
-
f"copilot task run --app {app.name} --env {env} "
|
|
227
|
-
f"--task-group-name {task_name} "
|
|
228
|
-
f"{execution_role}"
|
|
229
|
-
f"--image {CONDUIT_DOCKER_IMAGE_LOCATION}:{addon_type} "
|
|
230
|
-
f"--secrets CONNECTION_SECRET={get_connection_secret_arn(app, env, secret_name)} "
|
|
231
|
-
"--platform-os linux "
|
|
232
|
-
"--platform-arch arm64",
|
|
233
|
-
shell=True,
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def addon_client_is_running(app: Application, env: str, cluster_arn: str, task_name: str) -> bool:
|
|
238
|
-
ecs_client = app.environments[env].session.client("ecs")
|
|
239
|
-
|
|
240
|
-
tasks = ecs_client.list_tasks(
|
|
241
|
-
cluster=cluster_arn,
|
|
242
|
-
desiredStatus="RUNNING",
|
|
243
|
-
family=f"copilot-{task_name}",
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
if not tasks["taskArns"]:
|
|
247
|
-
return False
|
|
248
|
-
|
|
249
|
-
described_tasks = ecs_client.describe_tasks(cluster=cluster_arn, tasks=tasks["taskArns"])
|
|
250
|
-
|
|
251
|
-
# The ExecuteCommandAgent often takes longer to start running than the task and without the
|
|
252
|
-
# agent it's not possible to exec into a task.
|
|
253
|
-
for task in described_tasks["tasks"]:
|
|
254
|
-
for container in task["containers"]:
|
|
255
|
-
for agent in container["managedAgents"]:
|
|
256
|
-
if agent["name"] == "ExecuteCommandAgent" and agent["lastStatus"] == "RUNNING":
|
|
257
|
-
return True
|
|
258
|
-
|
|
259
|
-
return False
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def connect_to_addon_client_task(app: Application, env: str, cluster_arn: str, task_name: str):
|
|
263
|
-
tries = 0
|
|
264
|
-
running = False
|
|
265
|
-
|
|
266
|
-
while tries < 15 and not running:
|
|
267
|
-
tries += 1
|
|
268
|
-
|
|
269
|
-
if addon_client_is_running(app, env, cluster_arn, task_name):
|
|
270
|
-
running = True
|
|
271
|
-
subprocess.call(
|
|
272
|
-
"copilot task exec "
|
|
273
|
-
f"--app {app.name} --env {env} "
|
|
274
|
-
f"--name {task_name} "
|
|
275
|
-
f"--command bash",
|
|
276
|
-
shell=True,
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
time.sleep(1)
|
|
280
|
-
|
|
281
|
-
if not running:
|
|
282
|
-
raise CreateTaskTimeoutConduitError
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def add_stack_delete_policy_to_task_role(app: Application, env: str, task_name: str):
|
|
286
|
-
session = app.environments[env].session
|
|
287
|
-
cloudformation_client = session.client("cloudformation")
|
|
288
|
-
iam_client = session.client("iam")
|
|
289
|
-
|
|
290
|
-
conduit_stack_name = f"task-{task_name}"
|
|
291
|
-
conduit_stack_resources = cloudformation_client.list_stack_resources(
|
|
292
|
-
StackName=conduit_stack_name
|
|
293
|
-
)["StackResourceSummaries"]
|
|
294
|
-
|
|
295
|
-
for resource in conduit_stack_resources:
|
|
296
|
-
if resource["LogicalResourceId"] == "DefaultTaskRole":
|
|
297
|
-
task_role_name = resource["PhysicalResourceId"]
|
|
298
|
-
iam_client.put_role_policy(
|
|
299
|
-
RoleName=task_role_name,
|
|
300
|
-
PolicyName="DeleteCloudFormationStack",
|
|
301
|
-
PolicyDocument=json.dumps(
|
|
302
|
-
{
|
|
303
|
-
"Version": "2012-10-17",
|
|
304
|
-
"Statement": [
|
|
305
|
-
{
|
|
306
|
-
"Action": ["cloudformation:DeleteStack"],
|
|
307
|
-
"Effect": "Allow",
|
|
308
|
-
"Resource": f"arn:aws:cloudformation:*:*:stack/{conduit_stack_name}/*",
|
|
309
|
-
},
|
|
310
|
-
],
|
|
311
|
-
},
|
|
312
|
-
),
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def update_conduit_stack_resources(
|
|
317
|
-
app: Application,
|
|
318
|
-
env: str,
|
|
319
|
-
addon_type: str,
|
|
320
|
-
addon_name: str,
|
|
321
|
-
task_name: str,
|
|
322
|
-
parameter_name: str,
|
|
323
|
-
access: str,
|
|
324
|
-
):
|
|
325
|
-
session = app.environments[env].session
|
|
326
|
-
cloudformation_client = session.client("cloudformation")
|
|
327
|
-
|
|
328
|
-
conduit_stack_name = f"task-{task_name}"
|
|
329
|
-
template = cloudformation_client.get_template(StackName=conduit_stack_name)
|
|
330
|
-
template_yml = load_yaml(template["TemplateBody"])
|
|
331
|
-
template_yml["Resources"]["LogGroup"]["DeletionPolicy"] = "Retain"
|
|
332
|
-
template_yml["Resources"]["TaskNameParameter"] = load_yaml(
|
|
333
|
-
f"""
|
|
334
|
-
Type: AWS::SSM::Parameter
|
|
335
|
-
Properties:
|
|
336
|
-
Name: {parameter_name}
|
|
337
|
-
Type: String
|
|
338
|
-
Value: {task_name}
|
|
339
|
-
"""
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
iam_client = session.client("iam")
|
|
343
|
-
log_filter_role_arn = iam_client.get_role(RoleName="CWLtoSubscriptionFilterRole")["Role"]["Arn"]
|
|
344
|
-
|
|
345
|
-
ssm_client = session.client("ssm")
|
|
346
|
-
destination_log_group_arns = json.loads(
|
|
347
|
-
ssm_client.get_parameter(Name="/copilot/tools/central_log_groups")["Parameter"]["Value"]
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
destination_arn = destination_log_group_arns["dev"]
|
|
351
|
-
if env.lower() in ("prod", "production"):
|
|
352
|
-
destination_arn = destination_log_group_arns["prod"]
|
|
353
|
-
|
|
354
|
-
template_yml["Resources"]["SubscriptionFilter"] = load_yaml(
|
|
355
|
-
f"""
|
|
356
|
-
Type: AWS::Logs::SubscriptionFilter
|
|
357
|
-
DeletionPolicy: Retain
|
|
358
|
-
Properties:
|
|
359
|
-
RoleArn: {log_filter_role_arn}
|
|
360
|
-
LogGroupName: /copilot/{task_name}
|
|
361
|
-
FilterName: /copilot/conduit/{app.name}/{env}/{addon_type}/{addon_name}/{task_name.rsplit("-", 1)[1]}/{access}
|
|
362
|
-
FilterPattern: ''
|
|
363
|
-
DestinationArn: {destination_arn}
|
|
364
|
-
"""
|
|
365
|
-
)
|
|
366
|
-
|
|
367
|
-
params = []
|
|
368
|
-
if "Parameters" in template_yml:
|
|
369
|
-
for param in template_yml["Parameters"]:
|
|
370
|
-
params.append({"ParameterKey": param, "UsePreviousValue": True})
|
|
371
|
-
|
|
372
|
-
cloudformation_client.update_stack(
|
|
373
|
-
StackName=conduit_stack_name,
|
|
374
|
-
TemplateBody=dump_yaml(template_yml),
|
|
375
|
-
Parameters=params,
|
|
376
|
-
Capabilities=["CAPABILITY_IAM"],
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
def start_conduit(
|
|
381
|
-
application: Application,
|
|
382
|
-
env: str,
|
|
383
|
-
addon_type: str,
|
|
384
|
-
addon_name: str,
|
|
385
|
-
access: str = "read",
|
|
386
|
-
):
|
|
387
|
-
cluster_arn = get_cluster_arn(application, env)
|
|
388
|
-
parameter_name = get_parameter_name(application, env, addon_type, addon_name, access)
|
|
389
|
-
task_name = get_or_create_task_name(application, env, addon_name, parameter_name)
|
|
390
|
-
|
|
391
|
-
if not addon_client_is_running(application, env, cluster_arn, task_name):
|
|
392
|
-
create_addon_client_task(application, env, addon_type, addon_name, task_name, access)
|
|
393
|
-
add_stack_delete_policy_to_task_role(application, env, task_name)
|
|
394
|
-
update_conduit_stack_resources(
|
|
395
|
-
application, env, addon_type, addon_name, task_name, parameter_name, access
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
connect_to_addon_client_task(application, env, cluster_arn, task_name)
|
|
399
|
-
|
|
400
|
-
|
|
401
21
|
@click.command(cls=ClickDocOptCommand)
|
|
402
22
|
@click.argument("addon_name", type=str, required=True)
|
|
403
|
-
@click.option("--app", help="
|
|
404
|
-
@click.option("--env", help="
|
|
23
|
+
@click.option("--app", help="Application name", required=True)
|
|
24
|
+
@click.option("--env", help="Environment name", required=True)
|
|
405
25
|
@click.option(
|
|
406
26
|
"--access",
|
|
407
27
|
default="read",
|
|
408
28
|
type=click.Choice(CONDUIT_ACCESS_OPTIONS),
|
|
409
|
-
help="Allow write or admin access to database addons",
|
|
29
|
+
help="Allow read, write or admin access to the database addons.",
|
|
410
30
|
)
|
|
411
31
|
def conduit(addon_name: str, app: str, env: str, access: str):
|
|
412
|
-
"""
|
|
32
|
+
"""Opens a shell for a given addon_name create a conduit connection to
|
|
33
|
+
interact with postgres, opensearch or redis."""
|
|
413
34
|
check_platform_helper_version_needs_update()
|
|
414
35
|
application = load_application(app)
|
|
415
36
|
|
|
416
37
|
try:
|
|
417
|
-
|
|
418
|
-
except
|
|
38
|
+
Conduit(application).start(env, addon_name, access)
|
|
39
|
+
except NoClusterError:
|
|
40
|
+
# TODO: Set exception message in the exceptions and just output the message in the command code, should be able to catch all errors in one block
|
|
41
|
+
click.secho(f"""No ECS cluster found for "{app}" in "{env}" environment.""", fg="red")
|
|
42
|
+
exit(1)
|
|
43
|
+
except SecretNotFoundError as err:
|
|
419
44
|
click.secho(
|
|
420
|
-
f"""No
|
|
45
|
+
f"""No secret called "{err}" for "{app}" in "{env}" environment.""",
|
|
421
46
|
fg="red",
|
|
422
47
|
)
|
|
423
48
|
exit(1)
|
|
424
|
-
except
|
|
49
|
+
except CreateTaskTimeoutError:
|
|
425
50
|
click.secho(
|
|
426
|
-
f"""
|
|
51
|
+
f"""Client ({addon_name}) ECS task has failed to start for "{app}" in "{env}" environment.""",
|
|
427
52
|
fg="red",
|
|
428
53
|
)
|
|
429
54
|
exit(1)
|
|
430
|
-
except
|
|
55
|
+
except ParameterNotFoundError:
|
|
431
56
|
click.secho(
|
|
432
|
-
f"""
|
|
57
|
+
f"""No parameter called "/copilot/applications/{app}/environments/{env}/addons". Try deploying the "{app}" "{env}" environment.""",
|
|
433
58
|
fg="red",
|
|
434
59
|
)
|
|
435
60
|
exit(1)
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
61
|
+
except AddonNotFoundError:
|
|
62
|
+
click.secho(
|
|
63
|
+
f"""Addon "{addon_name}" does not exist.""",
|
|
64
|
+
fg="red",
|
|
65
|
+
)
|
|
441
66
|
exit(1)
|
|
442
|
-
except
|
|
67
|
+
except InvalidAddonTypeError as err:
|
|
443
68
|
click.secho(
|
|
444
|
-
f"""
|
|
69
|
+
f"""Addon type "{err.addon_type}" is not supported, we support: {", ".join(CONDUIT_ADDON_TYPES)}.""",
|
|
445
70
|
fg="red",
|
|
446
71
|
)
|
|
447
72
|
exit(1)
|
|
448
|
-
except
|
|
73
|
+
except AddonTypeMissingFromConfigError:
|
|
449
74
|
click.secho(
|
|
450
|
-
f"""
|
|
75
|
+
f"""The configuration for the addon {addon_name}, is missconfigured and missing the addon type.""",
|
|
451
76
|
fg="red",
|
|
452
77
|
)
|
|
453
78
|
exit(1)
|
|
@@ -102,7 +102,7 @@ def list(app, env):
|
|
|
102
102
|
params = dict(Path=path, Recursive=False, WithDecryption=True, MaxResults=10)
|
|
103
103
|
secrets = []
|
|
104
104
|
|
|
105
|
-
# TODO: refactor shared code with get_ssm_secret_names
|
|
105
|
+
# TODO: refactor shared code with get_ssm_secret_names - Check if this is still valid
|
|
106
106
|
while True:
|
|
107
107
|
response = client.get_parameters_by_path(**params)
|
|
108
108
|
|
dbt_platform_helper/constants.py
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
PLATFORM_CONFIG_FILE = "platform-config.yml"
|
|
2
2
|
PLATFORM_HELPER_VERSION_FILE = ".platform-helper-version"
|
|
3
|
+
DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION = "5"
|
|
4
|
+
PLATFORM_HELPER_CACHE_FILE = ".platform-helper-config-cache.yml"
|
|
5
|
+
|
|
6
|
+
# Keys
|
|
3
7
|
CODEBASE_PIPELINES_KEY = "codebase_pipelines"
|
|
4
8
|
ENVIRONMENTS_KEY = "environments"
|
|
5
|
-
|
|
9
|
+
|
|
10
|
+
# Conduit
|
|
11
|
+
CONDUIT_ADDON_TYPES = [
|
|
12
|
+
"opensearch",
|
|
13
|
+
"postgres",
|
|
14
|
+
"redis",
|
|
15
|
+
]
|
|
16
|
+
CONDUIT_DOCKER_IMAGE_LOCATION = "public.ecr.aws/uktrade/tunnel"
|