Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SC-1603-jinja-enhancements #4629

Merged
merged 11 commits into from
Dec 15, 2023
Merged

Conversation

a-dubs
Copy link
Collaborator

@a-dubs a-dubs commented Nov 27, 2023

Proposed Commit Message

feat(jinja): better jinja feedback and error catching

When an error is encountered while trying to parse a jinja template, the traceback is automatically parsed to get the specific error and the line number that caused this error, and then this information is turned into a user-friendly single-line error message. 

The cloud-init runtime will log these error messages when jinja syntax errors are encountered in the cloud-config file. For the query, render, and schema commands, the nasty traceback is caught and a single line error message is given to the user instead.

Fixes GH-4360

PR Checklist:

  • My code follows the process laid out in the documentation
  • I have updated or added any unit tests accordingly
  • I have updated or added any documentation accordingly

@aciba90
Copy link
Contributor

aciba90 commented Nov 28, 2023

This PR follows up the work from #4510.

@a-dubs a-dubs force-pushed the SC-1603-jinja-enhancements branch from 8a54d17 to 5b92e7d Compare December 4, 2023 15:55
assert 1 == query.handle_args("anyname", args)
out, err = capsys.readouterr()
assert expected_error in caplog.text
assert expected_error in err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a-dubs I think we determined in our discussion today that this unittest shouldn't be checking stderr setup because the query module no longer invokes setup_basic_logging directly to add a stderr stream handler to logging. This is now done up in cloudinit.cmd.main before calling this submodule gets invoked

Since the logging setup is outside of this scope of this unit, I'd suggest we have a supplemental test that asserts that main does the proper setup_basic_logging on all subcommands not in ('modules', 'init'). We already have separate unittest coverage that setup_basic_logging properly sets up a stderr handler here. So no need to add extra stderr validation if we minimally assert when setup_basic_logging is called below.

--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -2,6 +2,7 @@
 
 import contextlib
 import io
+import logging
 import os
 from collections import namedtuple
 
@@ -172,6 +173,45 @@ class TestCLI:
         for subcommand in expected_subcommands:
             assert subcommand in err
 
+    @pytest.mark.parametrize(
+        "subcommand,log_to_stderr,mocks",
+        (
+            ("init", False, [mock.patch("cloudinit.cmd.main.status_wrapper")]),
+            (
+                "modules",
+                False,
+                [mock.patch("cloudinit.cmd.main.status_wrapper")],
+            ),
+            (
+                "schema",
+                True,
+                [
+                    mock.patch(
+                        "cloudinit.stages.Init._read_cfg", return_value={}
+                    ),
+                    mock.patch("cloudinit.config.schema.handle_schema_args"),
+                ],
+            ),
+        ),
+    )
+    @mock.patch("cloudinit.cmd.main.setup_basic_logging")
+    def test_subcommands_log_to_stderr_via_setup_basic_logging(
+        self, setup_basic_logging, subcommand, log_to_stderr, mocks
+    ):
+        """setup_basic_logging is called for modules to use stderr
+
+        Subcommands with exception of 'init'  and 'modules' use
+        setup_basic_logging to direct logged errors to stderr.
+        """
+        with contextlib.ExitStack() as mockstack:
+            for mymock in mocks:
+                mockstack.enter_context(mymock)
+            self._call_main(["cloud-init", subcommand])
+        if log_to_stderr:
+            setup_basic_logging.assert_called_once_with(logging.WARNING)
+        else:
+            setup_basic_logging.assert_not_called()
+
     @pytest.mark.parametrize("subcommand", ["init", "modules"])
     @mock.patch("cloudinit.cmd.main.status_wrapper")
     def test_modules_subcommand_parser(self, m_status_wrapper, subcommand):

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch has been applied. Thank you!

@a-dubs a-dubs force-pushed the SC-1603-jinja-enhancements branch from 5b92e7d to 52d4a92 Compare December 4, 2023 22:38
@a-dubs a-dubs force-pushed the SC-1603-jinja-enhancements branch from 8774058 to 8c0a2a2 Compare December 5, 2023 02:18
@a-dubs a-dubs marked this pull request as ready for review December 5, 2023 18:19
@blackboxsw blackboxsw self-assigned this Dec 5, 2023
Copy link
Collaborator

@blackboxsw blackboxsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a-dubs thanks for this confirmed much better error messages on CLI tooling for bogus jinja template formatting instead of Tracebacks on the CLI. I have a couple of suggestions throughout for simpler error handling, more clear unittest value validation and possibly a better way to declare JinjaSyntaxParsingException.

Let me know what you think.

$ cat > bogus-jinja-fmt.tmpl <<EOF
{% if  platform  == 'gce' -%}
YEP I'm Gce
{% else -%}
NOPE I AM: {{ platform }}
{% endif %-}
EOF

$ PYTHONPATH=. python3 cloudinit/cmd/main.py query --format "$(cat bogus-jinja-fmt.tmpl)" -i md.json
2023-12-06 20:19:43,919 - query.py[ERROR]: Failed to render templated data. Unable to parse Jinja template due to syntax error: expected token 'end of statement block', got '%' on line 6: {% endif %-}

What I thnk is missing

cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Show resolved Hide resolved
cloudinit/util.py Outdated Show resolved Hide resolved
tests/unittests/config/test_schema.py Outdated Show resolved Hide resolved
tests/unittests/test_templating.py Outdated Show resolved Hide resolved
@a-dubs a-dubs force-pushed the SC-1603-jinja-enhancements branch from fe53d6a to 74c8ffe Compare December 14, 2023 15:00
@a-dubs
Copy link
Collaborator Author

a-dubs commented Dec 14, 2023

This is ready for a re-review now.

@blackboxsw I did my best to take your suggested changes and modify them as I saw fit and not just blindly patch them in. Let me know if the changes I made are inline with what you were hoping to see implemented.

Copy link
Collaborator

@blackboxsw blackboxsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of other nits and questions. Thanks @a-dubs . I'll wrap up the review today.

cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
cloudinit/templater.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@blackboxsw blackboxsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @a-dubs for the cleanups here. This looks good and shows simplier errors for all of our subcommands when invalid jinja templates are provided.

@blackboxsw blackboxsw merged commit 4f60ff0 into canonical:main Dec 15, 2023
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants