From d645574839c5791a131d24d90df2b86d2643d9d5 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 30 Oct 2019 20:03:23 +0200 Subject: [PATCH] [Apache v2] AugeasBlockNode find_comments() implementation (#7457) * find_comments implementation and AugeasCommentNode creation * Use dummy value for ancestor * Add NotImplementedError when calling find_comments with exact parameter * Remove parameter 'exact' from find_comments interface * Fix comment --- certbot-apache/certbot_apache/augeasparser.py | 35 +++++++++++++++---- certbot-apache/certbot_apache/dualparser.py | 7 ++-- certbot-apache/certbot_apache/interfaces.py | 5 ++- .../certbot_apache/tests/augeasnode_test.py | 9 +++++ .../certbot_apache/tests/dualnode_test.py | 11 ++++++ 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/certbot-apache/certbot_apache/augeasparser.py b/certbot-apache/certbot_apache/augeasparser.py index ebbf4120f..04286ef4d 100644 --- a/certbot-apache/certbot_apache/augeasparser.py +++ b/certbot-apache/certbot_apache/augeasparser.py @@ -151,13 +151,21 @@ class AugeasBlockNode(AugeasDirectiveNode): return nodes - def find_comments(self, comment, exact=False): # pylint: disable=unused-argument - """Recursive search of DirectiveNodes from the sequence of children""" - new_metadata = {"augeasparser": self.parser} - return [AugeasCommentNode(comment=assertions.PASS, - ancestor=self, - filepath=assertions.PASS, - metadata=new_metadata)] + def find_comments(self, comment): + """ + Recursive search of DirectiveNodes from the sequence of children. + + :param str comment: Comment content to search for. + """ + + nodes = list() + ownpath = self.metadata.get("augeaspath") + + comments = self.parser.find_comments(comment, start=ownpath) + for com in comments: + nodes.append(self._create_commentnode(com)) + + return nodes def delete_child(self, child): # pragma: no cover """Deletes a ParserNode from the sequence of children""" @@ -167,6 +175,19 @@ class AugeasBlockNode(AugeasDirectiveNode): """Returns a list of unsaved filepaths""" return [assertions.PASS] + def _create_commentnode(self, path): + """Helper function to create a CommentNode from Augeas path""" + + comment = self.parser.aug.get(path) + metadata = {"augeasparser": self.parser, "augeaspath": path} + + # Because of the dynamic nature of AugeasParser and the fact that we're + # not populating the complete node tree, the ancestor has a dummy value + return AugeasCommentNode(comment=comment, + ancestor=assertions.PASS, + filepath=apache_util.get_file_path(path), + metadata=metadata) + def _create_directivenode(self, path): """Helper function to create a DirectiveNode from Augeas path""" diff --git a/certbot-apache/certbot_apache/dualparser.py b/certbot-apache/certbot_apache/dualparser.py index d56e07de2..8897449b6 100644 --- a/certbot-apache/certbot_apache/dualparser.py +++ b/certbot-apache/certbot_apache/dualparser.py @@ -211,7 +211,7 @@ class DualBlockNode(DualNodeBase): return self._find_helper(DualDirectiveNode, "find_directives", name, exclude=exclude) - def find_comments(self, comment, exact=False): + def find_comments(self, comment): """ Performs a search for CommentNodes using both implementations and checks the results. This is built upon the assumption that unimplemented @@ -220,8 +220,7 @@ class DualBlockNode(DualNodeBase): instances that encapsulate the pairs of returned CommentNode objects. """ - return self._find_helper(DualCommentNode, "find_comments", comment, - exact=exact) + return self._find_helper(DualCommentNode, "find_comments", comment) def _find_helper(self, nodeclass, findfunc, search, **kwargs): """A helper for find_* functions. The function specific attributes should @@ -245,7 +244,7 @@ class DualBlockNode(DualNodeBase): if pass_primary and pass_secondary: # Both unimplemented new_nodes.append(nodeclass(primary=primary_res[0], - secondary=secondary_res[0])) + secondary=secondary_res[0])) # pragma: no cover elif pass_primary: for c in secondary_res: new_nodes.append(nodeclass(primary=primary_res[0], diff --git a/certbot-apache/certbot_apache/interfaces.py b/certbot-apache/certbot_apache/interfaces.py index cdfdfac91..ecad2d4eb 100644 --- a/certbot-apache/certbot_apache/interfaces.py +++ b/certbot-apache/certbot_apache/interfaces.py @@ -453,9 +453,9 @@ class BlockNode(DirectiveNode): """ @abc.abstractmethod - def find_comments(self, comment, exact=False): + def find_comments(self, comment): """ - Find comments with value containing or being exactly the same as search term. + Find comments with value containing the search term. This method walks the child tree of ParserNodes under the instance it was called from. This way it is possible to search for the whole configuration @@ -463,7 +463,6 @@ class BlockNode(DirectiveNode): from a specified branch. The lookup should be case sensitive. :param str comment: The content of comment to search for - :param bool exact: If the comment needs to exactly match the search term :returns: A list of found CommentNode objects. diff --git a/certbot-apache/certbot_apache/tests/augeasnode_test.py b/certbot-apache/certbot_apache/tests/augeasnode_test.py index c4631c57c..acb2d5329 100644 --- a/certbot-apache/certbot_apache/tests/augeasnode_test.py +++ b/certbot-apache/certbot_apache/tests/augeasnode_test.py @@ -63,3 +63,12 @@ class AugeasParserNodeTest(util.ApacheTest): self.assertEqual(servername[0].parameters[0], "certbot.demo") found = True self.assertTrue(found) + + def test_find_comments(self): + rootcomment = self.config.parser_root.find_comments( + "This is the main Apache server configuration file. " + ) + self.assertEqual(len(rootcomment), 1) + self.assertTrue(rootcomment[0].filepath.endswith( + "debian_apache_2_4/multiple_vhosts/apache2/apache2.conf" + )) diff --git a/certbot-apache/certbot_apache/tests/dualnode_test.py b/certbot-apache/certbot_apache/tests/dualnode_test.py index b37a7fdf9..3101a9c6f 100644 --- a/certbot-apache/certbot_apache/tests/dualnode_test.py +++ b/certbot-apache/certbot_apache/tests/dualnode_test.py @@ -161,6 +161,17 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public- self.block.primary) def test_find_comments(self): + pri_comments = [augeasparser.AugeasCommentNode(comment="some comment", + ancestor=self.block, + filepath="/path/to/whatever")] + sec_comments = [augeasparser.AugeasCommentNode(comment=assertions.PASS, + ancestor=self.block, + filepath=assertions.PASS)] + find_coms_primary = mock.MagicMock(return_value=pri_comments) + find_coms_secondary = mock.MagicMock(return_value=sec_comments) + self.block.primary.find_comments = find_coms_primary + self.block.secondary.find_comments = find_coms_secondary + dcoms = self.block.find_comments("comment") p_dcoms = [d.primary for d in dcoms] s_dcoms = [d.secondary for d in dcoms]