From 9f1740b14dac52cb0753be0811a0546a8f5da6bf Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sun, 9 Feb 2025 11:41:16 -0300 Subject: [PATCH 1/5] catch hcl parsing errors --- leverage/_utils.py | 22 ++++++++++++++++++++++ leverage/modules/auth.py | 12 +++++------- leverage/modules/terraform.py | 8 ++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/leverage/_utils.py b/leverage/_utils.py index 6e03e11..79c87c7 100644 --- a/leverage/_utils.py +++ b/leverage/_utils.py @@ -1,9 +1,12 @@ """ General use utilities. """ +from pathlib import Path from subprocess import run from subprocess import PIPE +import hcl2 +import lark from click.exceptions import Exit from configupdater import ConfigUpdater from docker import DockerClient @@ -112,6 +115,25 @@ def __init__(self, exit_code: int, error_description: str): super(ExitError, self).__init__(exit_code) +class ParsingError(ExitError): + """ + Raise an Exit exception when a file filed to be parsed. + """ + + +def parse_tf_file(file: Path): + """ + Open and parse an HCL file. + In case of a parsing error, raise a user-friendly error. + """ + with open(file) as f: + try: + parsed = hcl2.load(f) + except lark.exceptions.UnexpectedInput: + raise ExitError(1, f"There is a parsing error with the {f.name} file. Please review it.") + else: + return parsed + class ContainerSession: """ Handle the start/stop cycle of a container. diff --git a/leverage/modules/auth.py b/leverage/modules/auth.py index 8a4b74e..1a14afa 100644 --- a/leverage/modules/auth.py +++ b/leverage/modules/auth.py @@ -2,13 +2,13 @@ from pathlib import Path from configparser import NoSectionError, NoOptionError -import hcl2 import boto3 -from configupdater import ConfigUpdater +import hcl2 from botocore.exceptions import ClientError +from configupdater import ConfigUpdater from leverage import logger -from leverage._utils import key_finder, ExitError, get_or_create_section +from leverage._utils import key_finder, ExitError, get_or_create_section, parse_tf_file class SkipProfile(Exception): @@ -66,8 +66,7 @@ def get_profiles(cli): # these are files from the layer we are currently on for name in ("config.tf", "locals.tf"): try: - with open(name) as tf_file: - tf_config = hcl2.load(tf_file) + tf_config = parse_tf_file(Path(name)) except FileNotFoundError: continue @@ -76,8 +75,7 @@ def get_profiles(cli): raw_profiles.update(set(key_finder(tf_config, "profile", "lookup"))) # the profile value from /config/backend.tfvars - with open(cli.paths.local_backend_tfvars) as backend_config_file: - backend_config = hcl2.load(backend_config_file) + backend_config = parse_tf_file(cli.paths.local_backend_tfvars) tf_profile = backend_config["profile"] return tf_profile, raw_profiles diff --git a/leverage/modules/terraform.py b/leverage/modules/terraform.py index d763972..8a0f94e 100644 --- a/leverage/modules/terraform.py +++ b/leverage/modules/terraform.py @@ -1,13 +1,13 @@ import re +from pathlib import Path from typing import Sequence import click -import hcl2 from click.exceptions import Exit from leverage import logger from leverage._internals import pass_container, pass_state -from leverage._utils import ExitError +from leverage._utils import ExitError, parse_tf_file from leverage.container import TerraformContainer from leverage.container import get_docker_client from leverage.modules.utils import env_var_option, mount_option, auth_mfa, auth_sso @@ -512,8 +512,8 @@ def _validate_layout(tf: TerraformContainer): logger.error("[red]✘ FAILED[/red]\n") valid_layout = False - backend_tfvars = tf.paths.account_config_dir / tf.paths.BACKEND_TF_VARS # TODO use paths.backend_tfvars instead? - backend_tfvars = hcl2.loads(backend_tfvars.read_text()) if backend_tfvars.exists() else {} + backend_tfvars = Path(tf.paths.local_backend_tfvars) + backend_tfvars = parse_tf_file(backend_tfvars) if backend_tfvars.exists() else {} logger.info("Checking [bold]backend.tfvars[/bold]:\n") names_prefix = f"{tf.project}-{account_name}" From 0f56d037aa1e68a5ea481eab3096f7e7ce93b3ec Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sun, 9 Feb 2025 12:05:50 -0300 Subject: [PATCH 2/5] unnecessary --- leverage/_utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/leverage/_utils.py b/leverage/_utils.py index 79c87c7..5ace162 100644 --- a/leverage/_utils.py +++ b/leverage/_utils.py @@ -115,12 +115,6 @@ def __init__(self, exit_code: int, error_description: str): super(ExitError, self).__init__(exit_code) -class ParsingError(ExitError): - """ - Raise an Exit exception when a file filed to be parsed. - """ - - def parse_tf_file(file: Path): """ Open and parse an HCL file. From 221624a98463378f39d5e3590ec30d718cebe6f4 Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sun, 9 Feb 2025 12:37:12 -0300 Subject: [PATCH 3/5] Update leverage/modules/auth.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- leverage/modules/auth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/leverage/modules/auth.py b/leverage/modules/auth.py index 1a14afa..1ea9c32 100644 --- a/leverage/modules/auth.py +++ b/leverage/modules/auth.py @@ -3,7 +3,6 @@ from configparser import NoSectionError, NoOptionError import boto3 -import hcl2 from botocore.exceptions import ClientError from configupdater import ConfigUpdater From d1ebccff805871aceb4f22914ca81df71a6f1756 Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sun, 9 Feb 2025 13:10:58 -0300 Subject: [PATCH 4/5] black --- leverage/__init__.py | 1 + leverage/_internals.py | 1 + leverage/_utils.py | 2 ++ leverage/conf.py | 1 + leverage/leverage.py | 1 + leverage/logger.py | 1 + leverage/modules/credentials.py | 1 + leverage/modules/project.py | 1 + leverage/modules/run.py | 1 + leverage/path.py | 1 + leverage/tasks.py | 1 + 11 files changed, 12 insertions(+) diff --git a/leverage/__init__.py b/leverage/__init__.py index ec5410b..97202d2 100644 --- a/leverage/__init__.py +++ b/leverage/__init__.py @@ -1,6 +1,7 @@ """ Binbash Leverage Command-line tool. """ + # pylint: disable=wrong-import-position __version__ = "0.0.0" diff --git a/leverage/_internals.py b/leverage/_internals.py index b0dff0e..b325095 100644 --- a/leverage/_internals.py +++ b/leverage/_internals.py @@ -1,6 +1,7 @@ """ Definitions for internal use of the cli. """ + from functools import wraps import click diff --git a/leverage/_utils.py b/leverage/_utils.py index 5ace162..3ebe6c5 100644 --- a/leverage/_utils.py +++ b/leverage/_utils.py @@ -1,6 +1,7 @@ """ General use utilities. """ + from pathlib import Path from subprocess import run from subprocess import PIPE @@ -128,6 +129,7 @@ def parse_tf_file(file: Path): else: return parsed + class ContainerSession: """ Handle the start/stop cycle of a container. diff --git a/leverage/conf.py b/leverage/conf.py index b5ee850..458aaae 100644 --- a/leverage/conf.py +++ b/leverage/conf.py @@ -1,6 +1,7 @@ """ Env variables loading utility. """ + from pathlib import Path from yaenv.core import Env diff --git a/leverage/leverage.py b/leverage/leverage.py index b5f2634..0360f9f 100644 --- a/leverage/leverage.py +++ b/leverage/leverage.py @@ -1,6 +1,7 @@ """ Binbash Leverage Command-line tool. """ + import click from leverage import __version__ diff --git a/leverage/logger.py b/leverage/logger.py index e40ef66..27306c9 100644 --- a/leverage/logger.py +++ b/leverage/logger.py @@ -1,6 +1,7 @@ """ Logging utilities. """ + import logging from functools import wraps diff --git a/leverage/modules/credentials.py b/leverage/modules/credentials.py index d2e71e2..883a8d3 100644 --- a/leverage/modules/credentials.py +++ b/leverage/modules/credentials.py @@ -1,6 +1,7 @@ """ Credentials managing module. """ + import csv import json import re diff --git a/leverage/modules/project.py b/leverage/modules/project.py index de00043..9d601e7 100644 --- a/leverage/modules/project.py +++ b/leverage/modules/project.py @@ -1,6 +1,7 @@ """ Module for managing Leverage projects. """ + import re from pathlib import Path from shutil import copy2 diff --git a/leverage/modules/run.py b/leverage/modules/run.py index 187263f..75712b2 100644 --- a/leverage/modules/run.py +++ b/leverage/modules/run.py @@ -1,6 +1,7 @@ """ Tasks running module. """ + import re import click diff --git a/leverage/path.py b/leverage/path.py index 9cbd5ef..90b1644 100644 --- a/leverage/path.py +++ b/leverage/path.py @@ -1,6 +1,7 @@ """ Utilities to obtain relevant files' and directories' locations """ + import os from pathlib import Path from subprocess import CalledProcessError diff --git a/leverage/tasks.py b/leverage/tasks.py index da244c3..0e467be 100644 --- a/leverage/tasks.py +++ b/leverage/tasks.py @@ -1,6 +1,7 @@ """ Task loading, Task object definition and task creation decorator. """ + import sys import importlib from pathlib import Path From 4575b64cae798b6645953b334180b198999827ea Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sun, 9 Feb 2025 13:26:43 -0300 Subject: [PATCH 5/5] fix test --- tests/test_modules/test_auth.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_modules/test_auth.py b/tests/test_modules/test_auth.py index 0692f98..004afc0 100644 --- a/tests/test_modules/test_auth.py +++ b/tests/test_modules/test_auth.py @@ -1,4 +1,5 @@ from collections import namedtuple +from pathlib import PosixPath from unittest import mock from unittest.mock import Mock, MagicMock, PropertyMock @@ -190,8 +191,8 @@ def test_get_layer_profile(muted_click_context): """ data_dict = { - "config.tf": FILE_CONFIG_TF, - "locals.tf": FILE_LOCALS_TF, + PosixPath("config.tf"): FILE_CONFIG_TF, + PosixPath("locals.tf"): FILE_LOCALS_TF, "~/config/backend.tfvars": FILE_BACKEND_TFVARS, "~/.aws/test/config": FILE_AWS_CONFIG, "~/.aws/test/credentials": FILE_AWS_CREDENTIALS,