From 5c588a6f8ddc7bc5f13b79e90138c6d88c68b0e0 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 10 Dec 2019 20:20:00 +0200 Subject: [PATCH] [Apache v2] Implement parsed_files (#7562) * Implement parsed_files * Add parsed_files stub to ApacheParserNodes and fix assertions * Update certbot-apache/certbot_apache/interfaces.py Co-Authored-By: ohemorange * Add more descriptive comments * Update certbot-apache/certbot_apache/augeasparser.py Co-Authored-By: ohemorange * Update certbot-apache/certbot_apache/dualparser.py Co-Authored-By: ohemorange * Update certbot-apache/certbot_apache/interfaces.py Co-Authored-By: ohemorange --- certbot-apache/certbot_apache/apacheparser.py | 4 ++++ certbot-apache/certbot_apache/assertions.py | 16 ++++++++++++++++ certbot-apache/certbot_apache/augeasparser.py | 14 ++++++++++++++ certbot-apache/certbot_apache/dualparser.py | 19 +++++++++++++++++-- certbot-apache/certbot_apache/interfaces.py | 12 ++++++++++++ .../certbot_apache/tests/augeasnode_test.py | 4 ++++ .../certbot_apache/tests/dualnode_test.py | 19 +++++++++++++++++++ 7 files changed, 86 insertions(+), 2 deletions(-) diff --git a/certbot-apache/certbot_apache/apacheparser.py b/certbot-apache/certbot_apache/apacheparser.py index d9f33f095..969a4ab69 100644 --- a/certbot-apache/certbot_apache/apacheparser.py +++ b/certbot-apache/certbot_apache/apacheparser.py @@ -159,6 +159,10 @@ class ApacheBlockNode(ApacheDirectiveNode): """Returns a list of unsaved filepaths""" return [assertions.PASS] + def parsed_paths(self): # pragma: no cover + """Returns a list of parsed configuration file paths""" + return [assertions.PASS] + interfaces.CommentNode.register(ApacheCommentNode) interfaces.DirectiveNode.register(ApacheDirectiveNode) diff --git a/certbot-apache/certbot_apache/assertions.py b/certbot-apache/certbot_apache/assertions.py index fc2b35e14..c7a61f446 100644 --- a/certbot-apache/certbot_apache/assertions.py +++ b/certbot-apache/certbot_apache/assertions.py @@ -1,4 +1,6 @@ """Dual parser node assertions""" +import fnmatch + from certbot_apache import interfaces @@ -102,3 +104,17 @@ def assertEqualSimple(first, second): """ Simple assertion """ if not isPass(first) and not isPass(second): assert first == second + +def assertEqualPathsList(first, second): # pragma: no cover + """ + Checks that the two lists of file paths match. This assertion allows for wildcard + paths. + """ + if any([isPass(path) for path in first]): + return + if any([isPass(path) for path in second]): + return + for fpath in first: + assert any([fnmatch.fnmatch(fpath, spath) for spath in second]) + for spath in second: + assert any([fnmatch.fnmatch(fpath, spath) for fpath in first]) diff --git a/certbot-apache/certbot_apache/augeasparser.py b/certbot-apache/certbot_apache/augeasparser.py index 8a3a37083..d2771c9d2 100644 --- a/certbot-apache/certbot_apache/augeasparser.py +++ b/certbot-apache/certbot_apache/augeasparser.py @@ -383,6 +383,20 @@ class AugeasBlockNode(AugeasDirectiveNode): """Returns a list of unsaved filepaths""" return self.parser.unsaved_files() + def parsed_paths(self): + """ + Returns a list of file paths that have currently been parsed into the parser + tree. The returned list may include paths with wildcard characters, for + example: ['/etc/apache2/conf.d/*.load'] + + This is typically called on the root node of the ParserNode tree. + + :returns: list of file paths of files that have been parsed + """ + + parsed_paths = self.parser.aug.match("/augeas/load/Httpd/incl") + return [self.parser.aug.get(path) for path in parsed_paths] + def _create_commentnode(self, path): """Helper function to create a CommentNode from Augeas path""" diff --git a/certbot-apache/certbot_apache/dualparser.py b/certbot-apache/certbot_apache/dualparser.py index 667462d34..ef0800782 100644 --- a/certbot-apache/certbot_apache/dualparser.py +++ b/certbot-apache/certbot_apache/dualparser.py @@ -54,7 +54,7 @@ class DualNodeBase(object): if pass_primary and pass_secondary: # Both unimplemented new_nodes.append(nodeclass(primary=primary_res[0], - secondary=secondary_res[0])) # pragma: no cover + secondary=secondary_res[0])) # pragma: no cover elif pass_primary: for c in secondary_res: new_nodes.append(nodeclass(primary=primary_res[0], @@ -272,7 +272,6 @@ class DualBlockNode(DualNodeBase): return self._find_helper(DualCommentNode, "find_comments", comment) - def delete_child(self, child): """Deletes a child from the ParserNode implementations. The actual ParserNode implementations are used here directly in order to be able @@ -289,3 +288,19 @@ class DualBlockNode(DualNodeBase): assertions.assertEqualSimple(primary_files, secondary_files) return primary_files + + def parsed_paths(self): + """ + Returns a list of file paths that have currently been parsed into the parser + tree. The returned list may include paths with wildcard characters, for + example: ['/etc/apache2/conf.d/*.load'] + + This is typically called on the root node of the ParserNode tree. + + :returns: list of file paths of files that have been parsed + """ + + primary_paths = self.primary.parsed_paths() + secondary_paths = self.secondary.parsed_paths() + assertions.assertEqualPathsList(primary_paths, secondary_paths) + return primary_paths diff --git a/certbot-apache/certbot_apache/interfaces.py b/certbot-apache/certbot_apache/interfaces.py index 2d25fad0f..1b67be5c8 100644 --- a/certbot-apache/certbot_apache/interfaces.py +++ b/certbot-apache/certbot_apache/interfaces.py @@ -502,3 +502,15 @@ class BlockNode(DirectiveNode): :returns: list of file paths of files that have been changed but not yet saved to disk. """ + + @abc.abstractmethod + def parsed_paths(self): + """ + Returns a list of file paths that have currently been parsed into the parser + tree. The returned list may include paths with wildcard characters, for + example: ['/etc/apache2/conf.d/*.load'] + + This is typically called on the root node of the ParserNode tree. + + :returns: list of file paths of files that have been parsed + """ diff --git a/certbot-apache/certbot_apache/tests/augeasnode_test.py b/certbot-apache/certbot_apache/tests/augeasnode_test.py index 043f5d248..849a468c9 100644 --- a/certbot-apache/certbot_apache/tests/augeasnode_test.py +++ b/certbot-apache/certbot_apache/tests/augeasnode_test.py @@ -292,6 +292,10 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public- "ThisRaisesErrorBecauseMissingParameters" ) + def test_parsed_paths(self): + paths = self.config.parser_root.parsed_paths() + self.assertEqual(len(paths), 6) + def test_find_ancestors(self): vhsblocks = self.config.parser_root.find_blocks("VirtualHost") macro_test = False diff --git a/certbot-apache/certbot_apache/tests/dualnode_test.py b/certbot-apache/certbot_apache/tests/dualnode_test.py index 9e5a5e9aa..b5ab588f0 100644 --- a/certbot-apache/certbot_apache/tests/dualnode_test.py +++ b/certbot-apache/certbot_apache/tests/dualnode_test.py @@ -413,6 +413,25 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public- self.assertFalse(self.directive == ne_directive) self.assertFalse(self.comment == ne_comment) + def test_parsed_paths(self): + mock_p = mock.MagicMock(return_value=['/path/file.conf', + '/another/path', + '/path/other.conf']) + mock_s = mock.MagicMock(return_value=['/path/*.conf', '/another/path']) + self.block.primary.parsed_paths = mock_p + self.block.secondary.parsed_paths = mock_s + self.block.parsed_paths() + self.assertTrue(mock_p.called) + self.assertTrue(mock_s.called) + + def test_parsed_paths_error(self): + mock_p = mock.MagicMock(return_value=['/path/file.conf']) + mock_s = mock.MagicMock(return_value=['/path/*.conf', '/another/path']) + self.block.primary.parsed_paths = mock_p + self.block.secondary.parsed_paths = mock_s + with self.assertRaises(AssertionError): + self.block.parsed_paths() + def test_find_ancestors(self): primarymock = mock.MagicMock(return_value=[]) secondarymock = mock.MagicMock(return_value=[])