dbt-platform-helper 11.0.1__py3-none-any.whl → 11.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.

@@ -0,0 +1,459 @@
1
+ import itertools
2
+ import random
3
+ import re
4
+ import string
5
+ from pathlib import Path
6
+ from typing import List
7
+ from typing import Union
8
+
9
+ import boto3
10
+ import click
11
+
12
+ from dbt_platform_helper.providers.load_balancers import ListenerNotFoundError
13
+ from dbt_platform_helper.providers.load_balancers import ListenerRuleNotFoundError
14
+ from dbt_platform_helper.providers.load_balancers import LoadBalancerNotFoundError
15
+ from dbt_platform_helper.providers.load_balancers import find_https_listener
16
+ from dbt_platform_helper.utils.application import Environment
17
+ from dbt_platform_helper.utils.application import Service
18
+ from dbt_platform_helper.utils.application import load_application
19
+
20
+
21
+ class MaintenancePageProvider:
22
+ def activate(self, app, env, svc, template, vpc):
23
+ application = load_application(app)
24
+ application_environment = get_app_environment(app, env)
25
+
26
+ if "*" in svc:
27
+ services = [
28
+ s for s in application.services.values() if s.kind == "Load Balanced Web Service"
29
+ ]
30
+ else:
31
+ all_services = [get_app_service(app, s) for s in list(svc)]
32
+ services = [s for s in all_services if s.kind == "Load Balanced Web Service"]
33
+
34
+ if not services:
35
+ click.secho(f"No services deployed yet to {app} environment {env}", fg="red")
36
+ raise click.Abort
37
+
38
+ try:
39
+ https_listener = find_https_listener(application_environment.session, app, env)
40
+ current_maintenance_page = get_maintenance_page(
41
+ application_environment.session, https_listener
42
+ )
43
+ remove_current_maintenance_page = False
44
+ if current_maintenance_page:
45
+ remove_current_maintenance_page = click.confirm(
46
+ f"There is currently a '{current_maintenance_page}' maintenance page for the {env} "
47
+ f"environment in {app}.\nWould you like to replace it with a '{template}' "
48
+ f"maintenance page?"
49
+ )
50
+ if not remove_current_maintenance_page:
51
+ raise click.Abort
52
+
53
+ if remove_current_maintenance_page or click.confirm(
54
+ f"You are about to enable the '{template}' maintenance page for the {env} "
55
+ f"environment in {app}.\nWould you like to continue?"
56
+ ):
57
+ if current_maintenance_page and remove_current_maintenance_page:
58
+ remove_maintenance_page(application_environment.session, https_listener)
59
+
60
+ allowed_ips = get_env_ips(vpc, application_environment)
61
+
62
+ add_maintenance_page(
63
+ application_environment.session,
64
+ https_listener,
65
+ app,
66
+ env,
67
+ services,
68
+ allowed_ips,
69
+ template,
70
+ )
71
+ click.secho(
72
+ f"Maintenance page '{template}' added for environment {env} in application {app}",
73
+ fg="green",
74
+ )
75
+ else:
76
+ raise click.Abort
77
+
78
+ except LoadBalancerNotFoundError:
79
+ click.secho(
80
+ f"No load balancer found for environment {env} in the application {app}.", fg="red"
81
+ )
82
+ raise click.Abort
83
+
84
+ except ListenerNotFoundError:
85
+ click.secho(
86
+ f"No HTTPS listener found for environment {env} in the application {app}.", fg="red"
87
+ )
88
+ raise click.Abort
89
+
90
+ def deactivate(self, app, env):
91
+ application_environment = get_app_environment(app, env)
92
+
93
+ try:
94
+ https_listener = find_https_listener(application_environment.session, app, env)
95
+ current_maintenance_page = get_maintenance_page(
96
+ application_environment.session, https_listener
97
+ )
98
+ if not current_maintenance_page:
99
+ click.secho("There is no current maintenance page to remove", fg="red")
100
+ raise click.Abort
101
+
102
+ if not click.confirm(
103
+ f"There is currently a '{current_maintenance_page}' maintenance page, "
104
+ f"would you like to remove it?"
105
+ ):
106
+ raise click.Abort
107
+
108
+ remove_maintenance_page(application_environment.session, https_listener)
109
+ click.secho(
110
+ f"Maintenance page removed from environment {env} in application {app}", fg="green"
111
+ )
112
+
113
+ except LoadBalancerNotFoundError:
114
+ click.secho(
115
+ f"No load balancer found for environment {env} in the application {app}.", fg="red"
116
+ )
117
+ raise click.Abort
118
+
119
+ except ListenerNotFoundError:
120
+ click.secho(
121
+ f"No HTTPS listener found for environment {env} in the application {app}.", fg="red"
122
+ )
123
+ raise click.Abort
124
+
125
+
126
+ def get_app_service(app_name: str, svc_name: str) -> Service:
127
+ application = load_application(app_name)
128
+ application_service = application.services.get(svc_name)
129
+
130
+ if not application_service:
131
+ click.secho(
132
+ f"The service {svc_name} was not found in the application {app_name}. "
133
+ f"It either does not exist, or has not been deployed.",
134
+ fg="red",
135
+ )
136
+ raise click.Abort
137
+
138
+ return application_service
139
+
140
+
141
+ def get_app_environment(app_name: str, env_name: str) -> Environment:
142
+ application = load_application(app_name)
143
+ application_environment = application.environments.get(env_name)
144
+
145
+ if not application_environment:
146
+ click.secho(
147
+ f"The environment {env_name} was not found in the application {app_name}. "
148
+ f"It either does not exist, or has not been deployed.",
149
+ fg="red",
150
+ )
151
+ raise click.Abort
152
+
153
+ return application_environment
154
+
155
+
156
+ def get_maintenance_page(session: boto3.Session, listener_arn: str) -> Union[str, None]:
157
+ lb_client = session.client("elbv2")
158
+
159
+ rules = lb_client.describe_rules(ListenerArn=listener_arn)["Rules"]
160
+ tag_descriptions = get_rules_tag_descriptions(rules, lb_client)
161
+
162
+ maintenance_page_type = None
163
+ for description in tag_descriptions:
164
+ tags = {t["Key"]: t["Value"] for t in description["Tags"]}
165
+ if tags.get("name") == "MaintenancePage":
166
+ maintenance_page_type = tags.get("type")
167
+
168
+ return maintenance_page_type
169
+
170
+
171
+ def remove_maintenance_page(session: boto3.Session, listener_arn: str):
172
+ lb_client = session.client("elbv2")
173
+
174
+ rules = lb_client.describe_rules(ListenerArn=listener_arn)["Rules"]
175
+ # TODO: The next line doesn't appear to do anything.
176
+ tag_descriptions = get_rules_tag_descriptions(rules, lb_client)
177
+ # TODO: In fact the following line seems to do the same but better.
178
+ tag_descriptions = lb_client.describe_tags(ResourceArns=[r["RuleArn"] for r in rules])[
179
+ "TagDescriptions"
180
+ ]
181
+
182
+ for name in ["MaintenancePage", "AllowedIps", "BypassIpFilter", "AllowedSourceIps"]:
183
+ deleted = delete_listener_rule(tag_descriptions, name, lb_client)
184
+
185
+ if name == "MaintenancePage" and not deleted:
186
+ raise ListenerRuleNotFoundError()
187
+
188
+
189
+ def get_rules_tag_descriptions(rules: list, lb_client):
190
+ tag_descriptions = []
191
+ chunk_size = 20
192
+
193
+ for i in range(0, len(rules), chunk_size):
194
+ chunk = rules[i : i + chunk_size]
195
+ resource_arns = [r["RuleArn"] for r in chunk]
196
+ response = lb_client.describe_tags(ResourceArns=resource_arns)
197
+ tag_descriptions.extend(response["TagDescriptions"])
198
+
199
+ return tag_descriptions
200
+
201
+
202
+ def delete_listener_rule(tag_descriptions: list, tag_name: str, lb_client: boto3.client):
203
+ current_rule_arn = None
204
+
205
+ for description in tag_descriptions:
206
+ tags = {t["Key"]: t["Value"] for t in description["Tags"]}
207
+ if tags.get("name") == tag_name:
208
+ current_rule_arn = description["ResourceArn"]
209
+ if current_rule_arn:
210
+ lb_client.delete_rule(RuleArn=current_rule_arn)
211
+
212
+ return current_rule_arn
213
+
214
+
215
+ def add_maintenance_page(
216
+ session: boto3.Session,
217
+ listener_arn: str,
218
+ app: str,
219
+ env: str,
220
+ services: List[Service],
221
+ allowed_ips: tuple,
222
+ template: str = "default",
223
+ ):
224
+ lb_client = session.client("elbv2")
225
+ maintenance_page_content = get_maintenance_page_template(template)
226
+ bypass_value = "".join(random.choices(string.ascii_lowercase + string.digits, k=12))
227
+
228
+ rule_priority = itertools.count(start=1)
229
+
230
+ for svc in services:
231
+ target_group_arn = find_target_group(app, env, svc.name, session)
232
+
233
+ # not all of an application's services are guaranteed to have been deployed to an environment
234
+ if not target_group_arn:
235
+ continue
236
+
237
+ for ip in allowed_ips:
238
+ create_header_rule(
239
+ lb_client,
240
+ listener_arn,
241
+ target_group_arn,
242
+ "X-Forwarded-For",
243
+ [ip],
244
+ "AllowedIps",
245
+ next(rule_priority),
246
+ )
247
+ create_source_ip_rule(
248
+ lb_client,
249
+ listener_arn,
250
+ target_group_arn,
251
+ [ip],
252
+ "AllowedSourceIps",
253
+ next(rule_priority),
254
+ )
255
+
256
+ create_header_rule(
257
+ lb_client,
258
+ listener_arn,
259
+ target_group_arn,
260
+ "Bypass-Key",
261
+ [bypass_value],
262
+ "BypassIpFilter",
263
+ next(rule_priority),
264
+ )
265
+
266
+ click.secho(
267
+ f"\nUse a browser plugin to add `Bypass-Key` header with value {bypass_value} to your requests. For more detail, visit https://platform.readme.trade.gov.uk/activities/holding-and-maintenance-pages/",
268
+ fg="green",
269
+ )
270
+
271
+ lb_client.create_rule(
272
+ ListenerArn=listener_arn,
273
+ Priority=next(rule_priority),
274
+ Conditions=[
275
+ {
276
+ "Field": "path-pattern",
277
+ "PathPatternConfig": {"Values": ["/*"]},
278
+ }
279
+ ],
280
+ Actions=[
281
+ {
282
+ "Type": "fixed-response",
283
+ "FixedResponseConfig": {
284
+ "StatusCode": "503",
285
+ "ContentType": "text/html",
286
+ "MessageBody": maintenance_page_content,
287
+ },
288
+ }
289
+ ],
290
+ Tags=[
291
+ {"Key": "name", "Value": "MaintenancePage"},
292
+ {"Key": "type", "Value": template},
293
+ ],
294
+ )
295
+
296
+
297
+ def get_maintenance_page_template(template) -> str:
298
+ template_contents = (
299
+ Path(__file__)
300
+ .parent.parent.joinpath(
301
+ f"templates/svc/maintenance_pages/{template}.html",
302
+ )
303
+ .read_text()
304
+ .replace("\n", "")
305
+ )
306
+
307
+ # [^\S]\s+ - Remove any space that is not preceded by a non-space character.
308
+ return re.sub(r"[^\S]\s+", "", template_contents)
309
+
310
+
311
+ def find_target_group(app: str, env: str, svc: str, session: boto3.Session) -> str:
312
+ rg_tagging_client = session.client("resourcegroupstaggingapi")
313
+ response = rg_tagging_client.get_resources(
314
+ TagFilters=[
315
+ {
316
+ "Key": "copilot-application",
317
+ "Values": [
318
+ app,
319
+ ],
320
+ "Key": "copilot-environment",
321
+ "Values": [
322
+ env,
323
+ ],
324
+ "Key": "copilot-service",
325
+ "Values": [
326
+ svc,
327
+ ],
328
+ },
329
+ ],
330
+ ResourceTypeFilters=[
331
+ "elasticloadbalancing:targetgroup",
332
+ ],
333
+ )
334
+ for resource in response["ResourceTagMappingList"]:
335
+ tags = {tag["Key"]: tag["Value"] for tag in resource["Tags"]}
336
+
337
+ if (
338
+ "copilot-service" in tags
339
+ and tags["copilot-service"] == svc
340
+ and "copilot-environment" in tags
341
+ and tags["copilot-environment"] == env
342
+ and "copilot-application" in tags
343
+ and tags["copilot-application"] == app
344
+ ):
345
+ return resource["ResourceARN"]
346
+
347
+ click.secho(
348
+ f"No target group found for application: {app}, environment: {env}, service: {svc}",
349
+ fg="red",
350
+ )
351
+
352
+ return None
353
+
354
+
355
+ def create_header_rule(
356
+ lb_client: boto3.client,
357
+ listener_arn: str,
358
+ target_group_arn: str,
359
+ header_name: str,
360
+ values: list,
361
+ rule_name: str,
362
+ priority: int,
363
+ ):
364
+ conditions = get_host_conditions(lb_client, listener_arn, target_group_arn)
365
+
366
+ # add new condition to existing conditions
367
+ combined_conditions = [
368
+ {
369
+ "Field": "http-header",
370
+ "HttpHeaderConfig": {"HttpHeaderName": header_name, "Values": values},
371
+ }
372
+ ] + conditions
373
+
374
+ lb_client.create_rule(
375
+ ListenerArn=listener_arn,
376
+ Priority=priority,
377
+ Conditions=combined_conditions,
378
+ Actions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
379
+ Tags=[
380
+ {"Key": "name", "Value": rule_name},
381
+ ],
382
+ )
383
+
384
+ click.secho(
385
+ f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\n\nIf request header {header_name} contains one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.",
386
+ fg="green",
387
+ )
388
+
389
+
390
+ def create_source_ip_rule(
391
+ lb_client: boto3.client,
392
+ listener_arn: str,
393
+ target_group_arn: str,
394
+ values: list,
395
+ rule_name: str,
396
+ priority: int,
397
+ ):
398
+ conditions = get_host_conditions(lb_client, listener_arn, target_group_arn)
399
+
400
+ # add new condition to existing conditions
401
+ combined_conditions = [
402
+ {
403
+ "Field": "source-ip",
404
+ "SourceIpConfig": {"Values": [value + "/32" for value in values]},
405
+ }
406
+ ] + conditions
407
+
408
+ lb_client.create_rule(
409
+ ListenerArn=listener_arn,
410
+ Priority=priority,
411
+ Conditions=combined_conditions,
412
+ Actions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
413
+ Tags=[
414
+ {"Key": "name", "Value": rule_name},
415
+ ],
416
+ )
417
+
418
+ click.secho(
419
+ f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\n\nIf request source ip matches one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.",
420
+ fg="green",
421
+ )
422
+
423
+
424
+ def get_host_conditions(lb_client: boto3.client, listener_arn: str, target_group_arn: str):
425
+ rules = lb_client.describe_rules(ListenerArn=listener_arn)["Rules"]
426
+
427
+ # Get current set of forwarding conditions for the target group
428
+ for rule in rules:
429
+ for action in rule["Actions"]:
430
+ if action["Type"] == "forward" and action["TargetGroupArn"] == target_group_arn:
431
+ conditions = rule["Conditions"]
432
+
433
+ # filter to host-header conditions
434
+ conditions = [
435
+ {i: condition[i] for i in condition if i != "Values"}
436
+ for condition in conditions
437
+ if condition["Field"] == "host-header"
438
+ ]
439
+
440
+ # remove internal hosts
441
+ conditions[0]["HostHeaderConfig"]["Values"] = [
442
+ v for v in conditions[0]["HostHeaderConfig"]["Values"]
443
+ ]
444
+
445
+ return conditions
446
+
447
+
448
+ def get_env_ips(vpc: str, application_environment: Environment) -> List[str]:
449
+ account_name = f"{application_environment.session.profile_name}-vpc"
450
+ vpc_name = vpc if vpc else account_name
451
+ ssm_client = application_environment.session.client("ssm")
452
+
453
+ try:
454
+ param_value = ssm_client.get_parameter(Name=f"/{vpc_name}/EGRESS_IPS")["Parameter"]["Value"]
455
+ except ssm_client.exceptions.ParameterNotFound:
456
+ click.secho(f"No parameter found with name: /{vpc_name}/EGRESS_IPS")
457
+ raise click.Abort
458
+
459
+ return [ip.strip() for ip in param_value.split(",")]
@@ -0,0 +1,51 @@
1
+ import boto3
2
+
3
+
4
+ def find_load_balancer(session: boto3.Session, app: str, env: str) -> str:
5
+ lb_client = session.client("elbv2")
6
+
7
+ describe_response = lb_client.describe_load_balancers()
8
+ load_balancers = [lb["LoadBalancerArn"] for lb in describe_response["LoadBalancers"]]
9
+
10
+ load_balancers = lb_client.describe_tags(ResourceArns=load_balancers)["TagDescriptions"]
11
+
12
+ load_balancer_arn = None
13
+ for lb in load_balancers:
14
+ tags = {t["Key"]: t["Value"] for t in lb["Tags"]}
15
+ if tags.get("copilot-application") == app and tags.get("copilot-environment") == env:
16
+ load_balancer_arn = lb["ResourceArn"]
17
+
18
+ if not load_balancer_arn:
19
+ raise LoadBalancerNotFoundError()
20
+
21
+ return load_balancer_arn
22
+
23
+
24
+ def find_https_listener(session: boto3.Session, app: str, env: str) -> str:
25
+ load_balancer_arn = find_load_balancer(session, app, env)
26
+ lb_client = session.client("elbv2")
27
+ listeners = lb_client.describe_listeners(LoadBalancerArn=load_balancer_arn)["Listeners"]
28
+
29
+ listener_arn = None
30
+
31
+ try:
32
+ listener_arn = next(l["ListenerArn"] for l in listeners if l["Protocol"] == "HTTPS")
33
+ except StopIteration:
34
+ pass
35
+
36
+ if not listener_arn:
37
+ raise ListenerNotFoundError()
38
+
39
+ return listener_arn
40
+
41
+
42
+ class LoadBalancerNotFoundError(Exception):
43
+ pass
44
+
45
+
46
+ class ListenerNotFoundError(Exception):
47
+ pass
48
+
49
+
50
+ class ListenerRuleNotFoundError(Exception):
51
+ pass
@@ -0,0 +1,52 @@
1
+ # {% extra_header %}
2
+ # {% version_info %}
3
+ locals {
4
+ platform_config = yamldecode(file("../../../platform-config.yml"))
5
+ all_pipelines = local.platform_config["environment_pipelines"]
6
+ pipelines = { for pipeline, config in local.platform_config["environment_pipelines"] : pipeline => config if config.account == "{{ aws_account }}" }
7
+ environment_config = local.platform_config["environments"]
8
+ }
9
+
10
+ provider "aws" {
11
+ region = "eu-west-2"
12
+ profile = "{{ aws_account }}"
13
+ alias = "{{ aws_account }}"
14
+ shared_credentials_files = ["~/.aws/config"]
15
+ }
16
+
17
+ terraform {
18
+ required_version = "~> 1.8"
19
+ backend "s3" {
20
+ bucket = "terraform-platform-state-{{ aws_account }}"
21
+ key = "tfstate/application/{{ application }}-pipelines.tfstate"
22
+ region = "eu-west-2"
23
+ encrypt = true
24
+ kms_key_id = "alias/terraform-platform-state-s3-key-{{ aws_account }}"
25
+ dynamodb_table = "terraform-platform-lockdb-{{ aws_account }}"
26
+ }
27
+ required_providers {
28
+ aws = {
29
+ source = "hashicorp/aws"
30
+ version = "~> 5"
31
+ }
32
+ }
33
+ }
34
+
35
+
36
+ module "environment-pipelines" {
37
+ source = "git::https://github.com/uktrade/terraform-platform-modules.git//environment-pipelines?depth=1&ref={{ terraform_platform_modules_version }}"
38
+
39
+ for_each = local.pipelines
40
+
41
+ application = "{{ application }}"
42
+ pipeline_name = each.key
43
+ repository = "uktrade/{{ application }}-deploy"
44
+
45
+ environments = each.value.environments
46
+ all_pipelines = local.all_pipelines
47
+ environment_config = local.environment_config
48
+ branch = {% if deploy_branch %}"{{ deploy_branch }}"{% else %}each.value.branch{% endif %}
49
+ slack_channel = each.value.slack_channel
50
+ trigger_on_push = each.value.trigger_on_push
51
+ pipeline_to_trigger = lookup(each.value, "pipeline_to_trigger", null)
52
+ }
@@ -7,6 +7,7 @@ from typing import Tuple
7
7
 
8
8
  import boto3
9
9
  import botocore
10
+ import botocore.exceptions
10
11
  import click
11
12
  import yaml
12
13
  from boto3 import Session
@@ -20,62 +21,71 @@ AWS_SESSION_CACHE = {}
20
21
 
21
22
 
22
23
  def get_aws_session_or_abort(aws_profile: str = None) -> boto3.session.Session:
23
- aws_profile = aws_profile if aws_profile else os.getenv("AWS_PROFILE")
24
+ REFRESH_TOKEN_MESSAGE = (
25
+ "To refresh this SSO session run `aws sso login` with the corresponding profile"
26
+ )
27
+ aws_profile = aws_profile or os.getenv("AWS_PROFILE")
24
28
  if aws_profile in AWS_SESSION_CACHE:
25
29
  return AWS_SESSION_CACHE[aws_profile]
26
30
 
27
- # Check that the aws profile exists and is set.
28
- click.secho(f"""Checking AWS connection for profile "{aws_profile}"...""", fg="cyan")
31
+ click.secho(f'Checking AWS connection for profile "{aws_profile}"...', fg="cyan")
29
32
 
30
33
  try:
31
34
  session = boto3.session.Session(profile_name=aws_profile)
35
+ sts = session.client("sts")
36
+ account_id, user_id = get_account_details(sts)
37
+ click.secho("Credentials are valid.", fg="green")
38
+
32
39
  except botocore.exceptions.ProfileNotFound:
33
- click.secho(f"""AWS profile "{aws_profile}" is not configured.""", fg="red")
34
- exit(1)
40
+ _handle_error(f'AWS profile "{aws_profile}" is not configured.')
35
41
  except botocore.exceptions.ClientError as e:
36
42
  if e.response["Error"]["Code"] == "ExpiredToken":
37
- click.secho(
38
- f"Credentials are NOT valid. \nPlease login with: aws sso login --profile {aws_profile}",
39
- fg="red",
43
+ _handle_error(
44
+ f"Credentials are NOT valid. \nPlease login with: aws sso login --profile {aws_profile}"
40
45
  )
41
- exit(1)
42
-
43
- sts = session.client("sts")
44
- try:
45
- account_id, user_id = get_account_details(sts)
46
- click.secho("Credentials are valid.", fg="green")
47
- except (
48
- botocore.exceptions.UnauthorizedSSOTokenError,
49
- botocore.exceptions.TokenRetrievalError,
50
- botocore.exceptions.SSOTokenLoadError,
51
- ):
52
- click.secho(
53
- "The SSO session associated with this profile has expired or is otherwise invalid."
54
- "To refresh this SSO session run `aws sso login` with the corresponding profile",
55
- fg="red",
46
+ except botocore.exceptions.NoCredentialsError:
47
+ _handle_error("There are no credentials set for this session.", REFRESH_TOKEN_MESSAGE)
48
+ except botocore.exceptions.UnauthorizedSSOTokenError:
49
+ _handle_error("The SSO Token used for this session is unauthorised.", REFRESH_TOKEN_MESSAGE)
50
+ except botocore.exceptions.TokenRetrievalError:
51
+ _handle_error("Unable to retrieve the Token for this session.", REFRESH_TOKEN_MESSAGE)
52
+ except botocore.exceptions.SSOTokenLoadError:
53
+ _handle_error(
54
+ "The SSO session associated with this profile has expired, is not set or is otherwise invalid.",
55
+ REFRESH_TOKEN_MESSAGE,
56
56
  )
57
- exit(1)
58
57
 
59
58
  alias_client = session.client("iam")
60
- account_name = alias_client.list_account_aliases()["AccountAliases"]
59
+ account_name = alias_client.list_account_aliases().get("AccountAliases", [])
60
+
61
+ _log_account_info(account_name, account_id)
62
+
63
+ click.echo(
64
+ click.style("User: ", fg="yellow")
65
+ + click.style(f"{user_id.split(':')[-1]}\n", fg="white", bold=True)
66
+ )
67
+
68
+ AWS_SESSION_CACHE[aws_profile] = session
69
+ return session
70
+
71
+
72
+ def _handle_error(message: str, refresh_token_message: str = None) -> None:
73
+ full_message = message + (" " + refresh_token_message if refresh_token_message else "")
74
+ click.secho(full_message, fg="red")
75
+ exit(1)
76
+
77
+
78
+ def _log_account_info(account_name: list, account_id: str) -> None:
61
79
  if account_name:
62
80
  click.echo(
63
81
  click.style("Logged in with AWS account: ", fg="yellow")
64
- + click.style(f"{account_name[0]}/{account_id}", fg="white", bold=True),
82
+ + click.style(f"{account_name[0]}/{account_id}", fg="white", bold=True)
65
83
  )
66
84
  else:
67
85
  click.echo(
68
86
  click.style("Logged in with AWS account id: ", fg="yellow")
69
- + click.style(f"{account_id}", fg="white", bold=True),
87
+ + click.style(f"{account_id}", fg="white", bold=True)
70
88
  )
71
- click.echo(
72
- click.style("User: ", fg="yellow")
73
- + click.style(f"{user_id.split(':')[-1]}\n", fg="white", bold=True),
74
- )
75
-
76
- AWS_SESSION_CACHE[aws_profile] = session
77
-
78
- return session
79
89
 
80
90
 
81
91
  class NoProfileForAccountIdError(Exception):
@@ -362,12 +372,12 @@ def get_connection_string(
362
372
 
363
373
 
364
374
  class Vpc:
365
- def __init__(self, subnets, security_groups):
375
+ def __init__(self, subnets: list[str], security_groups: list[str]):
366
376
  self.subnets = subnets
367
377
  self.security_groups = security_groups
368
378
 
369
379
 
370
- def get_vpc_info_by_name(session, app, env, vpc_name):
380
+ def get_vpc_info_by_name(session: Session, app: str, env: str, vpc_name: str) -> Vpc:
371
381
  ec2_client = session.client("ec2")
372
382
  vpc_response = ec2_client.describe_vpcs(Filters=[{"Name": "tag:Name", "Values": [vpc_name]}])
373
383