Compare commits

...

6 Commits

Author SHA1 Message Date
m0namon
f9749f64d8 temp testfixes 2020-01-21 17:17:16 -08:00
sydneyli
c0633ec3ac Implement ApacheBlockNode.delete_child 2020-01-21 16:20:57 -08:00
sydneyli
c9edb49267 Implement ApacheBlockNode.add_child_* 2020-01-21 16:20:32 -08:00
sydneyli
9cc15da90f Implement ApacheBlockNode constructor 2020-01-21 16:19:58 -08:00
m0namon
69bb92d698 Bump apacheconfig dependency to latest version and install dev version of apache for coverage tests 2020-01-21 16:02:17 -08:00
m0namon
bafa9e2eb0 Load apacheconfig dependency, gate behind flag 2020-01-15 10:29:36 -08:00
9 changed files with 135 additions and 34 deletions

View File

@@ -1,5 +1,9 @@
""" apacheconfig implementation of the ParserNode interfaces """
from functools import partial
from certbot import errors
from certbot_apache._internal import assertions
from certbot_apache._internal import interfaces
from certbot_apache._internal import parsernode_util as util
@@ -78,12 +82,60 @@ class ApacheDirectiveNode(ApacheParserNode):
return
def _parameters_from_string(text):
text = text.strip()
words = []
word = ""
quote = None
escape = False
for c in text:
if c.isspace() and not quote:
if word:
words.append(word)
word = ""
else:
word += c
if not escape:
if not quote and c in "\"\'":
quote = c
elif c == quote:
words.append(word[1:-1])
word = ""
quote = None
escape = c == "\\"
if word:
words.append(word)
return tuple(words)
class ApacheBlockNode(ApacheDirectiveNode):
""" apacheconfig implementation of BlockNode interface """
def __init__(self, **kwargs):
super(ApacheBlockNode, self).__init__(**kwargs)
self.children = ()
self._raw_children = self._raw
children = []
for raw_node in self._raw_children:
metadata = self.metadata.copy()
metadata['ac_ast'] = raw_node
if raw_node.typestring == "comment":
node = ApacheCommentNode(comment=raw_node.name[2:],
metadata=metadata, ancestor=self,
filepath=self.filepath)
elif raw_node.typestring == "block":
parameters = _parameters_from_string(raw_node.arguments)
node = ApacheBlockNode(name=raw_node.tag, parameters=parameters,
metadata=metadata, ancestor=self,
filepath=self.filepath, enabled=self.enabled)
else:
parameters = ()
if raw_node.value:
parameters = _parameters_from_string(raw_node.value)
node = ApacheDirectiveNode(name=raw_node.name, parameters=parameters,
metadata=metadata, ancestor=self,
filepath=self.filepath, enabled=self.enabled)
children.append(node)
self.children = tuple(children)
def __eq__(self, other): # pragma: no cover
if isinstance(other, self.__class__):
@@ -97,36 +149,44 @@ class ApacheBlockNode(ApacheDirectiveNode):
self.metadata == other.metadata)
return False
def add_child_block(self, name, parameters=None, position=None): # pylint: disable=unused-argument
def _add_child_thing(self, raw_string, partial_node, position):
position = len(self._raw_children) if not position else position
# Cap position to length to mimic AugeasNode behavior. TODO: document that this happens
position = min(len(self._raw_children), position)
raw_ast = self._raw_children.add(position, raw_string)
metadata = self.metadata.copy()
metadata['ac_ast'] = raw_ast
new_node = partial_node(ancestor=self, metadata=metadata, filepath=self.filepath)
# Update metadata
children = list(self.children)
children.insert(position, new_node)
self.children = tuple(children)
return new_node
def add_child_block(self, name, parameters=None, position=None):
"""Adds a new BlockNode to the sequence of children"""
new_block = ApacheBlockNode(name=assertions.PASS,
parameters=assertions.PASS,
ancestor=self,
filepath=assertions.PASS,
metadata=self.metadata)
self.children += (new_block,)
return new_block
parameters_str = " " + " ".join(parameters) if parameters else ""
if not parameters:
parameters = []
partial_block = partial(ApacheBlockNode, name=name,
parameters=tuple(parameters), enabled=self.enabled)
return self._add_child_thing("\n<%s%s>\n</%s>" % (name, parameters_str, name),
partial_block, position)
def add_child_directive(self, name, parameters=None, position=None): # pylint: disable=unused-argument
def add_child_directive(self, name, parameters=None, position=None):
"""Adds a new DirectiveNode to the sequence of children"""
new_dir = ApacheDirectiveNode(name=assertions.PASS,
parameters=assertions.PASS,
ancestor=self,
filepath=assertions.PASS,
metadata=self.metadata)
self.children += (new_dir,)
return new_dir
# pylint: disable=unused-argument
def add_child_comment(self, comment="", position=None): # pragma: no cover
parameters_str = " " + " ".join(parameters) if parameters else ""
if not parameters: # TODO (mona): test
parameters = [] # pragma: no cover
partial_block = partial(ApacheDirectiveNode, name=name,
parameters=tuple(parameters), enabled=self.enabled)
return self._add_child_thing("\n%s%s" % (name, parameters_str), partial_block, position)
def add_child_comment(self, comment="", position=None):
"""Adds a new CommentNode to the sequence of children"""
new_comment = ApacheCommentNode(comment=assertions.PASS,
ancestor=self,
filepath=assertions.PASS,
metadata=self.metadata)
self.children += (new_comment,)
return new_comment
partial_comment = partial(ApacheCommentNode, comment=comment)
return self._add_child_thing(comment, partial_comment, position)
def find_blocks(self, name, exclude=True): # pylint: disable=unused-argument
"""Recursive search of BlockNodes from the sequence of children"""
@@ -151,9 +211,22 @@ class ApacheBlockNode(ApacheDirectiveNode):
filepath=assertions.PASS,
metadata=self.metadata)]
# TODO (mona): test
def delete_child(self, child): # pragma: no cover
"""Deletes a ParserNode from the sequence of children"""
return
index = -1
i = None
for i, elem in enumerate(self.children):
if elem == child:
index = i
break
if index < 0:
raise errors.PluginError("Could not find child node to delete")
children_list = list(self.children)
thing = children_list.pop(i)
self.children = tuple(children_list)
self._raw_children.remove(i)
return thing
def unsaved_files(self): # pragma: no cover
"""Returns a list of unsaved filepaths"""

View File

@@ -12,6 +12,11 @@ import pkg_resources
import six
import zope.component
import zope.interface
try:
import apacheconfig
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
from acme import challenges
from acme.magic_typing import DefaultDict # pylint: disable=unused-import, no-name-in-module
@@ -261,7 +266,7 @@ class ApacheConfigurator(common.Installer):
pn_meta = {"augeasparser": self.parser,
"augeaspath": self.parser.get_root_augpath(),
"ac_ast": None}
if self.USE_PARSERNODE:
if self.USE_PARSERNODE and HAS_APACHECONFIG:
self.parser_root = self.get_parsernode_root(pn_meta)
self.parsed_paths = self.parser_root.parsed_paths()
@@ -369,6 +374,11 @@ class ApacheConfigurator(common.Installer):
apache_vars["modules"] = apache_util.parse_modules(self.option("ctl"))
metadata["apache_vars"] = apache_vars
with open(self.parser.loc["root"]) as f:
with apacheconfig.make_loader(writable=True,
**apacheconfig.flavors.NATIVE_APACHE) as loader:
metadata["ac_ast"] = loader.loads(f.read())
return dualparser.DualBlockNode(
name=assertions.PASS,
ancestor=None,
@@ -907,7 +917,7 @@ class ApacheConfigurator(common.Installer):
"""
v1_vhosts = self.get_virtual_hosts_v1()
if self.USE_PARSERNODE:
if self.USE_PARSERNODE and HAS_APACHECONFIG:
v2_vhosts = self.get_virtual_hosts_v2()
for v1_vh in v1_vhosts:

View File

@@ -19,7 +19,7 @@ install_requires = [
]
dev_extras = [
'apacheconfig>=0.3.1',
'apacheconfig>=0.3.2',
]
class PyTest(TestCommand):

View File

@@ -1,8 +1,15 @@
"""Tests for AugeasParserNode classes"""
import mock
import unittest
import util
try:
import apacheconfig # pylint: disable=import-error,unused-import
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot import errors
@@ -10,6 +17,7 @@ from certbot_apache._internal import assertions
@unittest.skipIf(not HAS_APACHECONFIG, reason='Tests require apacheconfig dependency')
class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-methods
"""Test AugeasParserNode using available test configurations"""
@@ -146,7 +154,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
self.assertTrue(found)
def test_add_child_comment(self):
newc = self.config.parser_root.primary.add_child_comment("The content")
newc = self.config.parser_root.add_child_comment("The content")
comments = self.config.parser_root.find_comments("The content")
self.assertEqual(len(comments), 1)
self.assertEqual(

View File

@@ -15,7 +15,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
parser_mock = mock.MagicMock()
parser_mock.aug.match.return_value = []
parser_mock.get_arg.return_value = []
self.metadata = {"augeasparser": parser_mock, "augeaspath": "/invalid", "ac_ast": None}
ast_mock = mock.MagicMock()
self.metadata = {"augeasparser": parser_mock, "augeaspath": "/invalid", "ac_ast": ast_mock}
self.block = dualparser.DualBlockNode(name="block",
ancestor=None,
filepath="/tmp/something",

View File

@@ -5,7 +5,14 @@ import mock
import util
try:
import apacheconfig
HAS_APACHECONFIG = True
except ImportError: # pragma: no cover
HAS_APACHECONFIG = False
@unittest.skipIf(not HAS_APACHECONFIG, reason='Tests require apacheconfig dependency')
class ConfiguratorParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-methods
"""Test AugeasParserNode using available test configurations"""

View File

@@ -3,7 +3,7 @@
# Some dev package versions specified here may be overridden by higher level constraints
# files during tests (eg. letsencrypt-auto-source/pieces/dependency-requirements.txt).
alabaster==0.7.10
apacheconfig==0.3.1
apacheconfig==0.3.2
apipkg==1.4
appnope==0.1.0
asn1crypto==0.22.0

View File

@@ -40,7 +40,7 @@ pytz==2012rc0
google-api-python-client==1.5.5
# Our setup.py constraints
apacheconfig==0.3.1
apacheconfig==0.3.2
cloudflare==1.5.1
cryptography==1.2.3
parsedatetime==1.3

View File

@@ -119,12 +119,14 @@ setenv =
basepython = python2.7
commands =
{[base]install_packages}
{[base]pip_install} certbot-apache[dev]
python tox.cover.py
[testenv:py37-cover]
basepython = python3.7
commands =
{[base]install_packages}
{[base]pip_install} certbot-apache[dev]
python tox.cover.py
[testenv:lint]