1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00

Refactor _add_directive into separate functions (#5786)

* Refactor _add_directive to separate functions

* UnspacedList isn't idempotent

* refactor parser in add_server_directives and update_or_add_server_directives

* update parser tests

* remove replace=False and add to update_or_add for replace=True in configurator

* remove replace=False and add to update_or_add for replace=True in http01

* update documentation
This commit is contained in:
ohemorange
2018-03-23 16:30:13 -07:00
committed by GitHub
parent 693cb1d162
commit 8d0d42a739
5 changed files with 92 additions and 77 deletions

View File

@@ -192,8 +192,8 @@ class NginxConfigurator(common.Installer):
cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path],
['\n ', 'ssl_certificate_key', ' ', key_path]]
self.parser.add_server_directives(vhost,
cert_directives, replace=True)
self.parser.update_or_add_server_directives(vhost,
cert_directives)
logger.info("Deploying Certificate to VirtualHost %s", vhost.filep)
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
@@ -344,7 +344,7 @@ class NginxConfigurator(common.Installer):
for name in vhost.names:
name_block[0].append(' ')
name_block[0].append(name)
self.parser.add_server_directives(vhost, name_block, replace=True)
self.parser.update_or_add_server_directives(vhost, name_block)
def _get_default_vhost(self, port):
vhost_list = self.parser.get_vhosts()
@@ -584,7 +584,7 @@ class NginxConfigurator(common.Installer):
# have it continue to do so.
if len(vhost.addrs) == 0:
listen_block = [['\n ', 'listen', ' ', self.DEFAULT_LISTEN_PORT]]
self.parser.add_server_directives(vhost, listen_block, replace=False)
self.parser.add_server_directives(vhost, listen_block)
if vhost.ipv6_enabled():
ipv6_block = ['\n ',
@@ -618,7 +618,7 @@ class NginxConfigurator(common.Installer):
])
self.parser.add_server_directives(
vhost, ssl_block, replace=False)
vhost, ssl_block)
##################################
# enhancement methods (IInstaller)
@@ -683,7 +683,7 @@ class NginxConfigurator(common.Installer):
['\n ', 'add_header', ' ', header_substring, ' '] +
constants.HEADER_ARGS[header_substring],
['\n']]
self.parser.add_server_directives(vhost, header_directives, replace=False)
self.parser.add_server_directives(vhost, header_directives)
def _add_redirect_block(self, vhost, domain):
"""Add redirect directive to vhost
@@ -691,7 +691,7 @@ class NginxConfigurator(common.Installer):
redirect_block = _redirect_block_for_domain(domain)
self.parser.add_server_directives(
vhost, redirect_block, replace=False, insert_at_top=True)
vhost, redirect_block, insert_at_top=True)
def _split_block(self, vhost, only_directives=None):
"""Splits this "virtual host" (i.e. this nginx server block) into
@@ -771,7 +771,7 @@ class NginxConfigurator(common.Installer):
# Add this at the bottom to get the right order of directives
return_404_directive = [['\n ', 'return', ' ', '404']]
self.parser.add_server_directives(http_vhost, return_404_directive, replace=False)
self.parser.add_server_directives(http_vhost, return_404_directive)
vhost = http_vhost
@@ -821,7 +821,7 @@ class NginxConfigurator(common.Installer):
try:
self.parser.add_server_directives(vhost,
stapling_directives, replace=False)
stapling_directives)
except errors.MisconfigurationError as error:
logger.debug(error)
raise errors.PluginError("An error occurred while enabling OCSP "

View File

@@ -199,9 +199,9 @@ class NginxHttp01(common.ChallengePerformer):
['return', ' ', '200', ' ', validation]]]]
self.configurator.parser.add_server_directives(vhost,
location_directive, replace=False)
location_directive)
rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)',
' ', '$1', ' ', 'break']]
self.configurator.parser.add_server_directives(vhost,
rewrite_directive, replace=False, insert_at_top=True)
rewrite_directive, insert_at_top=True)

View File

@@ -276,15 +276,13 @@ class NginxParser(object):
return False
def add_server_directives(self, vhost, directives, replace, insert_at_top=False):
"""Add or replace directives in the server block identified by vhost.
def add_server_directives(self, vhost, directives, insert_at_top=False):
"""Add directives to the server block identified by vhost.
This method modifies vhost to be fully consistent with the new directives.
..note :: If replace is True and the directive already exists, the first
instance will be replaced. Otherwise, the directive is added.
..note :: If replace is False nothing gets added if an identical
block exists already.
..note :: It's an error to try and add a nonrepeatable directive that already
exists in the config block with a conflicting value.
..todo :: Doesn't match server blocks whose server_name directives are
split across multiple conf files.
@@ -292,13 +290,34 @@ class NginxParser(object):
:param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost
whose information we use to match on
:param list directives: The directives to add
:param bool replace: Whether to only replace existing directives
:param bool insert_at_top: True if the directives need to be inserted at the top
of the server block instead of the bottom
"""
self._modify_server_directives(vhost,
functools.partial(_add_directives, directives, replace, insert_at_top))
functools.partial(_add_directives, directives, insert_at_top))
def update_or_add_server_directives(self, vhost, directives, insert_at_top=False):
"""Add or replace directives in the server block identified by vhost.
This method modifies vhost to be fully consistent with the new directives.
..note :: When a directive with the same name already exists in the
config block, the first instance will be replaced. Otherwise, the directive
will be appended/prepended to the config block as in add_server_directives.
..todo :: Doesn't match server blocks whose server_name directives are
split across multiple conf files.
:param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost
whose information we use to match on
:param list directives: The directives to add
:param bool insert_at_top: True if the directives need to be inserted at the top
of the server block instead of the bottom
"""
self._modify_server_directives(vhost,
functools.partial(_update_or_add_directives, directives, insert_at_top))
def remove_server_directives(self, vhost, directive_name, match_func=None):
"""Remove all directives of type directive_name.
@@ -524,26 +543,17 @@ def _is_ssl_on_directive(entry):
len(entry) == 2 and entry[0] == 'ssl' and
entry[1] == 'on')
def _add_directives(directives, replace, insert_at_top, block):
"""Adds or replaces directives in a config block.
When replace=False, it's an error to try and add a nonrepeatable directive that already
exists in the config block with a conflicting value.
When replace=True and a directive with the same name already exists in the
config block, the first instance will be replaced. Otherwise, the directive
will be added to the config block.
..todo :: Find directives that are in included files.
:param list directives: The new directives.
:param bool replace: Described above.
:param bool insert_at_top: Described above.
:param list block: The block to replace in
"""
def _add_directives(directives, insert_at_top, block):
"""Adds directives to a config block."""
for directive in directives:
_add_directive(block, directive, replace, insert_at_top)
_add_directive(block, directive, insert_at_top)
if block and '\n' not in block[-1]: # could be " \n " or ["\n"] !
block.append(nginxparser.UnspacedList('\n'))
def _update_or_add_directives(directives, insert_at_top, block):
"""Adds or replaces directives in a config block."""
for directive in directives:
_update_or_add_directive(block, directive, insert_at_top)
if block and '\n' not in block[-1]: # could be " \n " or ["\n"] !
block.append(nginxparser.UnspacedList('\n'))
@@ -601,28 +611,20 @@ def _find_location(block, directive_name, match_func=None):
return next((index for index, line in enumerate(block) \
if line and line[0] == directive_name and (match_func is None or match_func(line))), None)
def _add_directive(block, directive, replace, insert_at_top):
"""Adds or replaces a single directive in a config block.
def _is_whitespace_or_comment(directive):
"""Is this directive either a whitespace or comment directive?"""
return len(directive) == 0 or directive[0] == '#'
See _add_directives for more documentation.
"""
directive = nginxparser.UnspacedList(directive)
def is_whitespace_or_comment(directive):
"""Is this directive either a whitespace or comment directive?"""
return len(directive) == 0 or directive[0] == '#'
if is_whitespace_or_comment(directive):
def _add_directive(block, directive, insert_at_top):
if not isinstance(directive, nginxparser.UnspacedList):
directive = nginxparser.UnspacedList(directive)
if _is_whitespace_or_comment(directive):
# whitespace or comment
block.append(directive)
return
location = _find_location(block, directive[0])
if replace:
if location is not None:
block[location] = directive
comment_directive(block, location)
return
# Append or prepend directive. Fail if the name is not a repeatable directive name,
# and there is already a copy of that directive with a different value
# in the config file.
@@ -647,7 +649,7 @@ def _add_directive(block, directive, replace, insert_at_top):
for included_directive in included_directives:
included_dir_loc = _find_location(block, included_directive[0])
included_dir_name = included_directive[0]
if not is_whitespace_or_comment(included_directive) \
if not _is_whitespace_or_comment(included_directive) \
and not can_append(included_dir_loc, included_dir_name):
if block[included_dir_loc] != included_directive:
raise errors.MisconfigurationError(err_fmt.format(included_directive,
@@ -668,6 +670,27 @@ def _add_directive(block, directive, replace, insert_at_top):
elif block[location] != directive:
raise errors.MisconfigurationError(err_fmt.format(directive, block[location]))
def _update_directive(block, directive, location):
block[location] = directive
comment_directive(block, location)
def _update_or_add_directive(block, directive, insert_at_top):
if not isinstance(directive, nginxparser.UnspacedList):
directive = nginxparser.UnspacedList(directive)
if _is_whitespace_or_comment(directive):
# whitespace or comment
block.append(directive)
return
location = _find_location(block, directive[0])
# we can update directive
if location is not None:
_update_directive(block, directive, location)
return
_add_directive(block, directive, insert_at_top)
def _is_certbot_comment(directive):
return '#' in directive and COMMENT in directive

View File

@@ -113,8 +113,7 @@ class NginxConfiguratorTest(util.NginxTest):
None, [0])
self.config.parser.add_server_directives(
mock_vhost,
[['listen', ' ', '5001', ' ', 'ssl']],
replace=False)
[['listen', ' ', '5001', ' ', 'ssl']])
self.config.save()
# pylint: disable=protected-access
@@ -206,9 +205,9 @@ class NginxConfiguratorTest(util.NginxTest):
"example/chain.pem",
None)
@mock.patch('certbot_nginx.parser.NginxParser.add_server_directives')
def test_deploy_cert_raise_on_add_error(self, mock_add_server_directives):
mock_add_server_directives.side_effect = errors.MisconfigurationError()
@mock.patch('certbot_nginx.parser.NginxParser.update_or_add_server_directives')
def test_deploy_cert_raise_on_add_error(self, mock_update_or_add_server_directives):
mock_update_or_add_server_directives.side_effect = errors.MisconfigurationError()
self.assertRaises(
errors.PluginError,
self.config.deploy_cert,

View File

@@ -206,8 +206,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
mock_vhost.path = [0]
nparser.add_server_directives(mock_vhost,
[['foo', 'bar'], ['ssl_certificate',
'/etc/ssl/cert2.pem']],
replace=False)
'/etc/ssl/cert2.pem']])
nparser.remove_server_directives(mock_vhost, 'foo')
nparser.remove_server_directives(mock_vhost, 'ssl_certificate')
self.assertEqual(nparser.parsed[example_com],
@@ -226,8 +225,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
None, [10, 1, 9])
nparser.add_server_directives(mock_vhost,
[['foo', 'bar'], ['\n ', 'ssl_certificate', ' ',
'/etc/ssl/cert.pem']],
replace=False)
'/etc/ssl/cert.pem']])
ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem')
dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')])
self.assertEqual(1, len(re.findall(ssl_re, dump)))
@@ -239,10 +237,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
mock_vhost.path = [0]
nparser.add_server_directives(mock_vhost,
[['foo', 'bar'], ['ssl_certificate',
'/etc/ssl/cert2.pem']],
replace=False)
nparser.add_server_directives(mock_vhost, [['foo', 'bar']],
replace=False)
'/etc/ssl/cert2.pem']])
nparser.add_server_directives(mock_vhost, [['foo', 'bar']])
from certbot_nginx.parser import COMMENT
self.assertEqual(nparser.parsed[example_com],
[[['server'], [['listen', '69.50.225.155:9000'],
@@ -264,8 +260,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
nparser.add_server_directives,
mock_vhost,
[['foo', 'bar'],
['ssl_certificate', '/etc/ssl/cert2.pem']],
replace=False)
['ssl_certificate', '/etc/ssl/cert2.pem']])
def test_comment_is_repeatable(self):
nparser = parser.NginxParser(self.config_path)
@@ -275,12 +270,10 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
set(['.example.com', 'example.*']),
None, [0])
nparser.add_server_directives(mock_vhost,
[['\n ', '#', ' ', 'what a nice comment']],
replace=False)
[['\n ', '#', ' ', 'what a nice comment']])
nparser.add_server_directives(mock_vhost,
[['\n ', 'include', ' ',
nparser.abs_path('comment_in_file.conf')]],
replace=False)
nparser.abs_path('comment_in_file.conf')]])
from certbot_nginx.parser import COMMENT
self.assertEqual(nparser.parsed[example_com],
[[['server'], [['listen', '69.50.225.155:9000'],
@@ -299,8 +292,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
target = set(['.example.com', 'example.*'])
filep = nparser.abs_path('sites-enabled/example.com')
mock_vhost = obj.VirtualHost(filep, None, None, None, target, None, [0])
nparser.add_server_directives(
mock_vhost, [['server_name', 'foobar.com']], replace=True)
nparser.update_or_add_server_directives(
mock_vhost, [['server_name', 'foobar.com']])
from certbot_nginx.parser import COMMENT
self.assertEqual(
nparser.parsed[filep],
@@ -310,8 +303,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
['server_name', 'example.*'], []
]]])
mock_vhost.names = set(['foobar.com', 'example.*'])
nparser.add_server_directives(
mock_vhost, [['ssl_certificate', 'cert.pem']], replace=True)
nparser.update_or_add_server_directives(
mock_vhost, [['ssl_certificate', 'cert.pem']])
self.assertEqual(
nparser.parsed[filep],
[[['server'], [['listen', '69.50.225.155:9000'],