mirror of
https://github.com/certbot/certbot.git
synced 2026-06-07 07:42:08 -04:00
Implement find_ancestors
This commit is contained in:
parent
517ff5cb19
commit
d1de5a9d5e
6 changed files with 115 additions and 33 deletions
|
|
@ -75,7 +75,6 @@ from certbot_apache import parser
|
|||
from certbot_apache import parsernode_util as util
|
||||
|
||||
|
||||
|
||||
class AugeasParserNode(interfaces.ParserNode):
|
||||
""" Augeas implementation of ParserNode interface """
|
||||
|
||||
|
|
@ -100,6 +99,65 @@ class AugeasParserNode(interfaces.ParserNode):
|
|||
def save(self, msg):
|
||||
self.parser.save(msg)
|
||||
|
||||
def find_ancestors(self, name):
|
||||
"""
|
||||
Searches ancestor BlockNodes with a given name.
|
||||
|
||||
:param str name: Name of the BlockNode parent to search for
|
||||
|
||||
:returns: List of matching ancestor nodes.
|
||||
:rtype: list of AugeasBlockNode
|
||||
"""
|
||||
|
||||
ancestors = []
|
||||
|
||||
parent = self.metadata["augeaspath"]
|
||||
while True:
|
||||
# Get the path of ancestor node
|
||||
parent = parent.rpartition("/")[0]
|
||||
if not parent:
|
||||
break
|
||||
anc = self._create_blocknode(parent)
|
||||
if anc.name.lower() == name.lower():
|
||||
ancestors.append(anc)
|
||||
|
||||
return ancestors
|
||||
|
||||
def _create_blocknode(self, path):
|
||||
"""
|
||||
Helper function to create a BlockNode from Augeas path. This is used by
|
||||
AugeasParserNode.find_ancestors and AugeasBlockNode.
|
||||
and AugeasBlockNode.find_blocks
|
||||
|
||||
"""
|
||||
|
||||
name = self._aug_get_name(path)
|
||||
metadata = {"augeasparser": self.parser, "augeaspath": path}
|
||||
|
||||
# Because of the dynamic nature, and the fact that we're not populating
|
||||
# the complete ParserNode tree, we use the search parent as ancestor
|
||||
return AugeasBlockNode(name=name,
|
||||
ancestor=assertions.PASS,
|
||||
filepath=apache_util.get_file_path(path),
|
||||
metadata=metadata)
|
||||
|
||||
def _aug_get_name(self, path):
|
||||
"""
|
||||
Helper function to get name of a configuration block or variable from path.
|
||||
"""
|
||||
|
||||
# Remove the ending slash if any
|
||||
if path[-1] == "/": # pragma: no cover
|
||||
path = path[:-1]
|
||||
|
||||
# Get the block name
|
||||
name = path.split("/")[-1]
|
||||
|
||||
# remove [...], it's not allowed in Apache configuration and is used
|
||||
# for indexing within Augeas
|
||||
name = name.split("[")[0]
|
||||
return name
|
||||
|
||||
|
||||
class AugeasCommentNode(AugeasParserNode):
|
||||
""" Augeas implementation of CommentNode interface """
|
||||
|
|
@ -253,7 +311,7 @@ class AugeasBlockNode(AugeasDirectiveNode):
|
|||
self.children += (new_comment,)
|
||||
return new_comment
|
||||
|
||||
def find_blocks(self, name, exclude=True): # pylint: disable=unused-argument
|
||||
def find_blocks(self, name, exclude=True):
|
||||
"""Recursive search of BlockNodes from the sequence of children"""
|
||||
|
||||
nodes = list()
|
||||
|
|
@ -265,7 +323,7 @@ class AugeasBlockNode(AugeasDirectiveNode):
|
|||
|
||||
return nodes
|
||||
|
||||
def find_directives(self, name, exclude=True): # pylint: disable=unused-argument
|
||||
def find_directives(self, name, exclude=True):
|
||||
"""Recursive search of DirectiveNodes from the sequence of children"""
|
||||
|
||||
nodes = list()
|
||||
|
|
@ -343,19 +401,6 @@ class AugeasBlockNode(AugeasDirectiveNode):
|
|||
filepath=apache_util.get_file_path(path),
|
||||
metadata=metadata)
|
||||
|
||||
def _create_blocknode(self, path):
|
||||
"""Helper function to create a BlockNode from Augeas path"""
|
||||
|
||||
name = self._aug_get_name(path)
|
||||
metadata = {"augeasparser": self.parser, "augeaspath": path}
|
||||
|
||||
# Because of the dynamic nature, and the fact that we're not populating
|
||||
# the complete ParserNode tree, we use the search parent as ancestor
|
||||
return AugeasBlockNode(name=name,
|
||||
ancestor=assertions.PASS,
|
||||
filepath=apache_util.get_file_path(path),
|
||||
metadata=metadata)
|
||||
|
||||
def _aug_find_blocks(self, name):
|
||||
"""Helper function to perform a search to Augeas DOM tree to search
|
||||
configuration blocks with a given name"""
|
||||
|
|
@ -370,23 +415,6 @@ class AugeasBlockNode(AugeasDirectiveNode):
|
|||
name.lower() in os.path.basename(path).lower()])
|
||||
return blk_paths
|
||||
|
||||
def _aug_get_name(self, path):
|
||||
"""
|
||||
Helper function to get name of a configuration block or variable from path.
|
||||
"""
|
||||
|
||||
# Remove the ending slash if any
|
||||
if path[-1] == "/": # pragma: no cover
|
||||
path = path[:-1]
|
||||
|
||||
# Get the block name
|
||||
name = path.split("/")[-1]
|
||||
|
||||
# remove [...], it's not allowed in Apache configuration and is used
|
||||
# for indexing within Augeas
|
||||
name = name.split("[")[0]
|
||||
return name
|
||||
|
||||
def _aug_resolve_child_position(self, name, position):
|
||||
"""
|
||||
Helper function that iterates through the immediate children and figures
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@ class DualNodeBase(object):
|
|||
assertions.assertEqualSimple(firstval, secondval)
|
||||
return firstval
|
||||
|
||||
def find_ancestors(self, name):
|
||||
""" Traverses the ancestor tree and returns ancestors matching name """
|
||||
|
||||
primary_ancs = self.primary.find_ancestors(name)
|
||||
secondary_ancs = self.secondary.find_ancestors(name)
|
||||
assert primary_ancs == secondary_ancs
|
||||
return primary_ancs
|
||||
|
||||
|
||||
class DualCommentNode(DualNodeBase):
|
||||
""" Dual parser implementation of CommentNode interface """
|
||||
|
|
|
|||
|
|
@ -192,6 +192,18 @@ class ParserNode(object):
|
|||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_ancestors(self, name):
|
||||
"""
|
||||
Traverses the ancestor tree up, searching for BlockNodes with a specific
|
||||
name.
|
||||
|
||||
:param str name: Name of the ancestor BlockNode to search for
|
||||
|
||||
:returns: A list of ancestor BlockNodes that match the name
|
||||
:rtype: list of BlockNode
|
||||
"""
|
||||
|
||||
|
||||
# Linter rule exclusion done because of https://github.com/PyCQA/pylint/issues/179
|
||||
@six.add_metaclass(abc.ABCMeta) # pylint: disable=abstract-method
|
||||
|
|
|
|||
|
|
@ -281,3 +281,24 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
|
|||
self.config.parser_root.primary.add_child_directive,
|
||||
"ThisRaisesErrorBecauseMissingParameters"
|
||||
)
|
||||
|
||||
def test_find_ancestors(self):
|
||||
vhsblocks = self.config.parser_root.find_blocks("VirtualHost")
|
||||
macro_test = False
|
||||
nonmacro_test = False
|
||||
for vh in vhsblocks:
|
||||
if "/macro/" in vh.metadata["augeaspath"].lower():
|
||||
ancs = vh.find_ancestors("Macro")
|
||||
self.assertEqual(len(ancs), 1)
|
||||
macro_test = True
|
||||
else:
|
||||
ancs = vh.find_ancestors("Macro")
|
||||
self.assertEqual(len(ancs), 0)
|
||||
nonmacro_test = True
|
||||
self.assertTrue(macro_test)
|
||||
self.assertTrue(nonmacro_test)
|
||||
|
||||
def test_find_ancestors_bad_path(self):
|
||||
self.config.parser_root.primary.metadata["augeaspath"] = ""
|
||||
ancs = self.config.parser_root.primary.find_ancestors("Anything")
|
||||
self.assertEqual(len(ancs), 0)
|
||||
|
|
|
|||
|
|
@ -415,3 +415,12 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
|
|||
self.assertFalse(self.block == ne_block)
|
||||
self.assertFalse(self.directive == ne_directive)
|
||||
self.assertFalse(self.comment == ne_comment)
|
||||
|
||||
def test_find_ancestors(self):
|
||||
primarymock = mock.MagicMock(return_value=[])
|
||||
secondarymock = mock.MagicMock(return_value=[])
|
||||
self.block.primary.find_ancestors = primarymock
|
||||
self.block.secondary.find_ancestors = secondarymock
|
||||
self.block.find_ancestors("anything")
|
||||
self.assertTrue(primarymock.called)
|
||||
self.assertTrue(secondarymock.called)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class DummyParserNode(interfaces.ParserNode):
|
|||
"""Save"""
|
||||
pass
|
||||
|
||||
def find_ancestors(self, name): # pragma: no cover
|
||||
""" Find ancestors """
|
||||
return []
|
||||
|
||||
|
||||
class DummyCommentNode(DummyParserNode):
|
||||
""" A dummy class implementing CommentNode interface """
|
||||
|
|
|
|||
Loading…
Reference in a new issue