From 11774f003f5a501f52e6c6057ddb15c1e80b7824 Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Sat, 1 Feb 2025 10:40:10 -0300 Subject: [PATCH 1/2] [BV-267] Make sure .gitconfig is always present (#295) * make sure .gitconfig is always present * update integration tests --- .github/workflows/tests-integration.yaml | 2 +- Dockerfile | 2 -- leverage/container.py | 5 ++++- leverage/path.py | 4 ++++ tests/bats/leverage.bats | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests-integration.yaml b/.github/workflows/tests-integration.yaml index da9b11db..6b926238 100644 --- a/.github/workflows/tests-integration.yaml +++ b/.github/workflows/tests-integration.yaml @@ -61,7 +61,7 @@ jobs: run: | mkdir -p ../theadamproject # These are later mounted in the container - mkdir ~/.ssh && touch ~/.gitconfig + mkdir ~/.ssh - name: Project Init run: | diff --git a/Dockerfile b/Dockerfile index 8d1b0b94..bef7e5b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,6 @@ RUN git clone https://github.com/bats-core/bats-assert.git # Needed as is mounted later on RUN mkdir /root/.ssh -# Needed for git to run propertly -RUN touch /root/.gitconfig RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/usr/local POETRY_VERSION=1.8.2 python3 - diff --git a/leverage/container.py b/leverage/container.py index a8258ecb..1dc4f52f 100644 --- a/leverage/container.py +++ b/leverage/container.py @@ -455,6 +455,9 @@ def __init__(self, client, mounts=None, env_vars=None): # SSH AGENT SSH_AUTH_SOCK = os.getenv("SSH_AUTH_SOCK") + # make sure .gitconfig exists before mounting it + self.paths.host_git_config_file.touch(exist_ok=True) + self.environment.update( { "COMMON_CONFIG_FILE": self.paths.common_tfvars, @@ -479,7 +482,7 @@ def __init__(self, client, mounts=None, env_vars=None): target=self.paths.guest_aws_credentials_dir, type="bind", ), - Mount(source=(self.paths.home / ".gitconfig").as_posix(), target="/etc/gitconfig", type="bind"), + Mount(source=self.paths.host_git_config_file.as_posix(), target="/etc/gitconfig", type="bind"), ] self.mounts.extend(extra_mounts) # if you have set the tf plugin cache locally diff --git a/leverage/path.py b/leverage/path.py index 1a09d244..9cbd5efd 100644 --- a/leverage/path.py +++ b/leverage/path.py @@ -197,6 +197,10 @@ def host_aws_profiles_file(self): def host_aws_credentials_file(self): return self.host_aws_credentials_dir / "credentials" + @property + def host_git_config_file(self): + return self.home / ".gitconfig" + @property def local_backend_tfvars(self): return self.account_config_dir / self.BACKEND_TF_VARS diff --git a/tests/bats/leverage.bats b/tests/bats/leverage.bats index bbcbe487..f67cf947 100644 --- a/tests/bats/leverage.bats +++ b/tests/bats/leverage.bats @@ -35,7 +35,7 @@ teardown(){ cp "$BUILD_SCRIPTS/simple_build.py" "$ACC_DIR/build.py" cd "$ACC_DIR" - run leverage -l + run leverage run -l assert_line --index 0 "Tasks in build file \`build.py\`:" assert_line --index 1 --regexp "^ hello\s+Say hello.$" @@ -49,7 +49,7 @@ teardown(){ cp "$BUILD_SCRIPTS/simple_build.py" "$ROOT_DIR/build.py" cd "$ROOT_DIR/account" - run leverage -l + run leverage run -l assert_line --index 0 "Tasks in build file \`build.py\`:" assert_line --index 1 --regexp "^ hello\s+Say hello.$" From 9cbfa4c3315f22c72889c3b031cd91c029e8cfa4 Mon Sep 17 00:00:00 2001 From: Francisco Rivera Date: Mon, 10 Feb 2025 15:25:11 -0300 Subject: [PATCH 2/2] Enhancement | Catch and handle HCL parsing errors (#296) * catch hcl parsing errors * unnecessary * Update leverage/modules/auth.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * black * fix test --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- leverage/__init__.py | 1 + leverage/_internals.py | 1 + leverage/_utils.py | 18 ++++++++++++++++++ leverage/conf.py | 1 + leverage/leverage.py | 1 + leverage/logger.py | 1 + leverage/modules/auth.py | 11 ++++------- leverage/modules/credentials.py | 1 + leverage/modules/project.py | 1 + leverage/modules/run.py | 1 + leverage/modules/terraform.py | 8 ++++---- leverage/path.py | 1 + leverage/tasks.py | 1 + tests/test_modules/test_auth.py | 5 +++-- 14 files changed, 39 insertions(+), 13 deletions(-) diff --git a/leverage/__init__.py b/leverage/__init__.py index ec5410bf..97202d24 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 b0dff0e9..b3250952 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 6e03e114..3ebe6c51 100644 --- a/leverage/_utils.py +++ b/leverage/_utils.py @@ -1,9 +1,13 @@ """ 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 +116,20 @@ def __init__(self, exit_code: int, error_description: str): super(ExitError, self).__init__(exit_code) +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/conf.py b/leverage/conf.py index b5ee8507..458aaae3 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 b5f26342..0360f9ff 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 e40ef66b..27306c9c 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/auth.py b/leverage/modules/auth.py index 8a4b74e3..1ea9c32b 100644 --- a/leverage/modules/auth.py +++ b/leverage/modules/auth.py @@ -2,13 +2,12 @@ from pathlib import Path from configparser import NoSectionError, NoOptionError -import hcl2 import boto3 -from configupdater import ConfigUpdater 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 +65,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 +74,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/credentials.py b/leverage/modules/credentials.py index d2e71e2e..883a8d35 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 de00043b..9d601e7f 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 187263fc..75712b25 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/modules/terraform.py b/leverage/modules/terraform.py index d7639722..8a0f94e7 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}" diff --git a/leverage/path.py b/leverage/path.py index 9cbd5efd..90b1644b 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 da244c3a..0e467be5 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 diff --git a/tests/test_modules/test_auth.py b/tests/test_modules/test_auth.py index 0692f988..004afc0d 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,