binbashar / leverage

Binbash Leverage CLI intended to orchestrate Leverage Reference Architecture for AWS (www.binbash.co/leverage)
https://pypi.org/project/leverage/
Apache License 2.0
17 stars 2 forks source link

Bug | Syntax errors in layer's `config.tf` file cause leverage to fail #268

Open angelofenoglio opened 3 months ago

angelofenoglio commented 3 months ago

Describe the Bug

If there's a syntax error in the config.tf file in the layer, that is, the file holding the providers, backend and terraform remote state configuration blocks, leverage panics and displays an ugly and verbose error whenever a terraform command is run.

Expected Behavior

When extracting the profiles required to run a terraform command, if there's a syntax error in the config.tf file, the HCL parser will throw an error. Leverage should catch the error and show a friendly message.

Steps to Reproduce

Steps to reproduce the behavior:

  1. Go to any layer.
  2. Open the config.tf file.
  3. Remove a brace from a terraform remote state block.
  4. Run 'leverage tf plan'
  5. A lengthy, and barely understandable error and stack trace is shown.

Screenshots

I this example I'm running leverage tf shell --sso in a layer where I'm missing the closing brace to a terraform_remote_state datasource block.

$ leverage tf shell --sso
Traceback (most recent call last):
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parsers/lalr_parser.py", line 59, in get_action
    return states[state][token.type]
KeyError: '$END'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/bin/leverage", line 8, in <module>
    sys.exit(leverage())
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 1137, in __call__
    return self.main(*args, **kwargs)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 1062, in main
    rv = self.invoke(ctx)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 1668, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 1668, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/click/core.py", line 763, in invoke
    return __callback(*args, **kwargs)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/_internals.py", line 50, in new_command
    return command(ctx.obj.container, *args, **kwargs)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/modules/terraform.py", line 158, in shell
    tf.start_shell()
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/container.py", line 622, in start_shell
    with AwsCredsEntryPoint(self, override_entrypoint=""):
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/_utils.py", line 96, in __init__
    auth_method = container.auth_method()
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/container.py", line 507, in auth_method
    refresh_layer_credentials(self)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/modules/auth.py", line 86, in refresh_layer_credentials
    tf_profile, raw_profiles = get_profiles(cli)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/leverage/modules/auth.py", line 69, in get_profiles
    tf_config = hcl2.load(tf_file)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/hcl2/api.py", line 9, in load
    return loads(file.read())
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/hcl2/api.py", line 18, in loads
    return hcl2.parse(text + "\n")
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/lark.py", line 464, in parse
    return self.parser.parse(text, start=start)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parser_frontends.py", line 115, in parse
    return self._parse(token_stream, start)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parser_frontends.py", line 63, in _parse
    return self.parser.parse(input, start, *args)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parsers/lalr_parser.py", line 35, in parse
    return self.parser.parse(*args)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parsers/lalr_parser.py", line 111, in parse
    _action, arg = get_action(token)
  File "/home/user/.local/share/virtualenvs/devops-tf-infra-bS5XNOX1/lib/python3.8/site-packages/lark/parsers/lalr_parser.py", line 66, in get_action
    raise UnexpectedToken(token, expected, state=state, puppet=puppet)
lark.exceptions.UnexpectedToken: Unexpected token Token('$END', '') at line 98, column 1.
Expected one of: 
        * RBRACE

Environment

Additional Context

Originally, we used sed commands to parse the config.tf, and account.tf files to get the profiles needed by the layer so we can then obtain the required credentials. However we now use a parser that does the job for us. This greatly simplifies the job of hunting the strings of text in the file, but at the same time we now should make sure that we capture any error that the parsing produces to present understandable messages to the user.