Make common Installer base class (#5055)

* Add installer class

* Add wrapped reverter methods to common.Installer.

* Use Installer class in Apache plugin

* Use Installer class in Nginx plugin

* Don't create reverter in Apache and Nginx plugins
This commit is contained in:
Brad Warren
2017-08-28 17:06:09 -07:00
committed by ohemorange
parent 8d362d4469
commit ae0be73b53
4 changed files with 202 additions and 94 deletions
+98
View File
@@ -13,7 +13,9 @@ from acme.jose import util as jose_util
from certbot import constants
from certbot import crypto_util
from certbot import errors
from certbot import interfaces
from certbot import reverter
from certbot import util
logger = logging.getLogger(__name__)
@@ -100,6 +102,102 @@ class Plugin(object):
# other
class Installer(Plugin):
"""An installer base class with reverter methods defined.
Installer plugins do not have to inherit from this class.
"""
def __init__(self, *args, **kwargs):
super(Installer, self).__init__(*args, **kwargs)
self.reverter = reverter.Reverter(self.config)
def add_to_checkpoint(self, save_files, save_notes, temporary=False):
"""Add files to a checkpoint.
:param set save_files: set of filepaths to save
:param str save_notes: notes about changes during the save
:param bool temporary: True if the files should be added to a
temporary checkpoint rather than a permanent one. This is
usually used for changes that will soon be reverted.
:raises .errors.PluginError: when unable to add to checkpoint
"""
if temporary:
checkpoint_func = self.reverter.add_to_temp_checkpoint
else:
checkpoint_func = self.reverter.add_to_checkpoint
try:
checkpoint_func(save_files, save_notes)
except errors.ReverterError as err:
raise errors.PluginError(str(err))
def finalize_checkpoint(self, title):
"""Timestamp and save changes made through the reverter.
:param str title: Title describing checkpoint
:raises .errors.PluginError: when an error occurs
"""
try:
self.reverter.finalize_checkpoint(title)
except errors.ReverterError as err:
raise errors.PluginError(str(err))
def recovery_routine(self):
"""Revert all previously modified files.
Reverts all modified files that have not been saved as a checkpoint
:raises .errors.PluginError: If unable to recover the configuration
"""
try:
self.reverter.recovery_routine()
except errors.ReverterError as err:
raise errors.PluginError(str(err))
def revert_temporary_config(self):
"""Rollback temporary checkpoint.
:raises .errors.PluginError: when unable to revert config
"""
try:
self.reverter.revert_temporary_config()
except errors.ReverterError as err:
raise errors.PluginError(str(err))
def rollback_checkpoints(self, rollback=1):
"""Rollback saved checkpoints.
:param int rollback: Number of checkpoints to revert
:raises .errors.PluginError: If there is a problem with the input or
the function is unable to correctly revert the configuration
"""
try:
self.reverter.rollback_checkpoints(rollback)
except errors.ReverterError as err:
raise errors.PluginError(str(err))
def view_config_changes(self):
"""Show all of the configuration changes that have taken place.
:raises .errors.PluginError: If there is a problem while processing
the checkpoints directories.
"""
try:
self.reverter.view_config_changes()
except errors.ReverterError as err:
raise errors.PluginError(str(err))
class Addr(object):
r"""Represents an virtual host address.
+87
View File
@@ -1,4 +1,5 @@
"""Tests for certbot.plugins.common."""
import functools
import os
import shutil
import tempfile
@@ -12,6 +13,7 @@ from acme import jose
from certbot import achallenges
from certbot import crypto_util
from certbot import errors
from certbot.tests import acme_util
from certbot.tests import util as test_util
@@ -77,6 +79,91 @@ class PluginTest(unittest.TestCase):
"--mock-foo-bar", dest="different_to_foo_bar", x=1, y=None)
class InstallerTest(unittest.TestCase):
"""Tests for certbot.plugins.common.Installer."""
def setUp(self):
from certbot.plugins.common import Installer
with mock.patch("certbot.plugins.common.reverter.Reverter"):
self.installer = Installer(config=mock.MagicMock(),
name="Installer")
self.reverter = self.installer.reverter
def test_add_to_real_checkpoint(self):
files = set(("foo.bar", "baz.qux",))
save_notes = "foo bar baz qux"
self._test_wrapped_method("add_to_checkpoint", files, save_notes)
def test_add_to_real_checkpoint2(self):
self._test_add_to_checkpoint_common(False)
def test_add_to_temporary_checkpoint(self):
self._test_add_to_checkpoint_common(True)
def _test_add_to_checkpoint_common(self, temporary):
files = set(("foo.bar", "baz.qux",))
save_notes = "foo bar baz qux"
installer_func = functools.partial(self.installer.add_to_checkpoint,
temporary=temporary)
if temporary:
reverter_func = self.reverter.add_to_temp_checkpoint
else:
reverter_func = self.reverter.add_to_checkpoint
self._test_adapted_method(
installer_func, reverter_func, files, save_notes)
def test_finalize_checkpoint(self):
self._test_wrapped_method("finalize_checkpoint", "foo")
def test_recovery_routine(self):
self._test_wrapped_method("recovery_routine")
def test_revert_temporary_config(self):
self._test_wrapped_method("revert_temporary_config")
def test_rollback_checkpoints(self):
self._test_wrapped_method("rollback_checkpoints", 42)
def test_view_config_changes(self):
self._test_wrapped_method("view_config_changes")
def _test_wrapped_method(self, name, *args, **kwargs):
"""Test a wrapped reverter method.
:param str name: name of the method to test
:param tuple args: position arguments to method
:param dict kwargs: keyword arguments to method
"""
installer_func = getattr(self.installer, name)
reverter_func = getattr(self.reverter, name)
self._test_adapted_method(
installer_func, reverter_func, *args, **kwargs)
def _test_adapted_method(self, installer_func,
reverter_func, *passed_args, **passed_kwargs):
"""Test an adapted reverter method
:param callable installer_func: installer method to test
:param mock.MagicMock reverter_func: mocked adapated
reverter method
:param tuple passed_args: positional arguments passed from
installer method to the reverter method
:param dict passed_kargs: keyword arguments passed from
installer method to the reverter method
"""
installer_func(*passed_args, **passed_kwargs)
reverter_func.assert_called_once_with(*passed_args, **passed_kwargs)
reverter_func.side_effect = errors.ReverterError
self.assertRaises(
errors.PluginError, installer_func, *passed_args, **passed_kwargs)
class AddrTest(unittest.TestCase):
"""Tests for certbot.client.plugins.common.Addr."""