From 207bd6c31c7ce56e6a8f02a2b99b7d772567af9b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 3 Feb 2015 12:13:57 +0000 Subject: [PATCH] IConfig attributes. config/args separation --- letsencrypt/client/client.py | 14 ++++-- letsencrypt/client/configuration.py | 10 ++-- letsencrypt/client/interfaces.py | 39 ++++++++++++++- letsencrypt/client/tests/client_test.py | 2 +- letsencrypt/scripts/main.py | 64 ++++++++++++------------- 5 files changed, 84 insertions(+), 45 deletions(-) diff --git a/letsencrypt/client/client.py b/letsencrypt/client/client.py index 6cc1ad81c..7f48e80e0 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client/client.py @@ -421,7 +421,7 @@ def determine_installer(config): logging.info("Unable to find a way to install the certificate.") -def rollback(config): +def rollback(checkpoints, config): """Revert configuration the specified number of checkpoints. .. note:: If another installer uses something other than the reverter class @@ -436,6 +436,8 @@ def rollback(config): of future installers. Perhaps the interface should define errors that are thrown for the various functions. + :param int checkpoints: Number of checkpoints to revert. + :param config: Configuration. :type config: :class:`letsencrypt.client.interfaces.IConfig` @@ -444,20 +446,22 @@ def rollback(config): try: installer = determine_installer(config) except errors.LetsEncryptMisconfigurationError: - _misconfigured_rollback(config) + _misconfigured_rollback(checkpoints, config) return # No Errors occurred during init... proceed normally # If installer is None... couldn't find an installer... there shouldn't be # anything to rollback if installer is not None: - installer.rollback_checkpoints(config.rollback) + installer.rollback_checkpoints(checkpoints) installer.restart() -def _misconfigured_rollback(config): +def _misconfigured_rollback(checkpoints, config): """Handles the case where the Installer is misconfigured. + :param int checkpoints: Number of checkpoints to revert. + :param config: Configuration. :type config: :class:`letsencrypt.client.interfaces.IConfig` @@ -477,7 +481,7 @@ def _misconfigured_rollback(config): # Also... not sure how future installers will handle recovery. rev = reverter.Reverter(config) rev.recovery_routine() - rev.rollback_checkpoints(config.rollback) + rev.rollback_checkpoints(checkpoints) # We should try to restart the server try: diff --git a/letsencrypt/client/configuration.py b/letsencrypt/client/configuration.py index a39bf4732..5b7568c1f 100644 --- a/letsencrypt/client/configuration.py +++ b/letsencrypt/client/configuration.py @@ -1,6 +1,8 @@ """Let's Encrypt user-supplied configuration.""" +import os import zope.interface +from letsencrypt.client import constants from letsencrypt.client import interfaces @@ -15,21 +17,21 @@ class NamespaceConfig(object): return getattr(self.namespace, name) @property - def temp_checkpoint_dir(self): + def temp_checkpoint_dir(self): # pylint: disable=missing-docstring return os.path.join( self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR_NAME) @property - def in_progress_dir(self): + def in_progress_dir(self): # pylint: disable=missing-docstring return os.path.join( self.namespace.work_dir, constants.IN_PROGRESS_DIR_NAME) @property - def cert_key_backup(self): + def cert_key_backup(self): # pylint: disable=missing-docstring return os.path.join( self.namespace.work_dir, constants.CERT_KEY_BACKUP_DIR_NAME) @property - def rev_tokens_dir(self): + def rev_tokens_dir(self): # pylint: disable=missing-docstring return os.path.join( self.namespace.work_dir, constants.REV_TOKENS_DIR_NAME) diff --git a/letsencrypt/client/interfaces.py b/letsencrypt/client/interfaces.py index f9c614614..9516ba95d 100644 --- a/letsencrypt/client/interfaces.py +++ b/letsencrypt/client/interfaces.py @@ -60,7 +60,44 @@ class IChallenge(zope.interface.Interface): class IConfig(zope.interface.Interface): - """Let's Encrypt uesr-supplied configuration.""" + """Let's Encrypt user-supplied configuration.""" + + acme_server = zope.interface.Attribute( + "CA hostname (and optionally :port). The server certificate must " + "be trusted in order to avoid further modifications to the client.") + rsa_key_size = zope.interface.Attribute("Size of the RSA key.") + + config_dir = zope.interface.Attribute("Configuration directory.") + work_dir = zope.interface.Attribute("Working directory.") + backup_dir = zope.interface.Attribute("Configuration backups directory.") + temp_checkpoint_dir = zope.interface.Attribute( + "Temporary checkpoint directory.") + in_progress_dir = zope.interface.Attribute( + "Directory used before a permanent checkpoint is finalized.") + cert_key_backup = zope.interface.Attribute( + "Directory where all certificates and keys are stored. " + "Used for easy revocation.") + rev_tokens_dir = zope.interface.Attribute( + "Directory where all revocation tokens are saved.") + key_dir = zope.interface.Attribute("Keys storage.") + cert_dir = zope.interface.Attribute("Certificates storage.") + + le_vhost_ext = zope.interface.Attribute( + "SSL vhost configuration extension.") + cert_path = zope.interface.Attribute("Let's Encrypt certificate file.") + chain_path = zope.interface.Attribute("Let's Encrypt chain file.") + + apache_server_root = zope.interface.Attribute( + "Apache server root directory.") + apache_ctl = zope.interface.Attribute( + "Path to the 'apache2ctl' binary, used for 'configtest' and " + "retrieving Apache2 version number.") + apache_enmod = zope.interface.Attribute( + "Path to the Apache 'a2enmod' binary.") + apache_init_script = zope.interface.Attribute( + "Path to the Apache init script (used for server reload/restart).") + apache_mod_ssl_conf = zope.interface.Attribute( + "Contains standard Apache SSL directives.") class IInstaller(zope.interface.Interface): diff --git a/letsencrypt/client/tests/client_test.py b/letsencrypt/client/tests/client_test.py index 6b9235e11..df07d1fa9 100644 --- a/letsencrypt/client/tests/client_test.py +++ b/letsencrypt/client/tests/client_test.py @@ -14,7 +14,7 @@ class RollbackTest(unittest.TestCase): @classmethod def _call(cls, checkpoints): from letsencrypt.client.client import rollback - rollback(mock.MagicMock(rollback=checkpoints)) + rollback(checkpoints, mock.MagicMock()) @mock.patch("letsencrypt.client.client.determine_installer") def test_no_problems(self, mock_det): diff --git a/letsencrypt/scripts/main.py b/letsencrypt/scripts/main.py index 62e007128..7eeb8ebe7 100755 --- a/letsencrypt/scripts/main.py +++ b/letsencrypt/scripts/main.py @@ -14,7 +14,6 @@ import zope.component import letsencrypt -from letsencrypt.client import constants from letsencrypt.client import configuration from letsencrypt.client import client from letsencrypt.client import display @@ -28,17 +27,16 @@ def create_parser(): description="letsencrypt client %s" % letsencrypt.__version__) add = parser.add_argument + config_help = lambda name: interfaces.IConfig[name].__doc__ add("-d", "--domains", metavar="DOMAIN", nargs="+") add("-s", "--acme-server", "--server", default="letsencrypt-demo.org:443", - help="CA hostname (and optionally :port). The server certificate must " - "be trusted in order to avoid further modifications to the " - "client.") + help=config_help("acme_server")) add("-p", "--privkey", type=read_file, help="Path to the private key file for certificate generation.") - add("-B", "--rsa-key-size", type=int, default=2048, - metavar="N", help="RSA key shall be sized N bits.") + add("-B", "--rsa-key-size", type=int, default=2048, metavar="N", + help=config_help("rsa_key_size")) add("-k", "--revoke", action="store_true", help="Revoke a certificate.") add("-b", "--rollback", type=int, default=0, metavar="N", @@ -56,33 +54,31 @@ def create_parser(): add("--test", action="store_true", help="Run in test mode.") add("--config-dir", default="/etc/letsencrypt", - help="Configuration directory.") + help=config_help("config_dir")) add("--work-dir", default="/var/lib/letsencrypt", - help="Working directory.") + help=config_help("work_dir")) add("--backup-dir", default="/var/lib/letsencrypt/backups", - help="Configuration backups directory.") - add("--key-dir", default="/etc/letsencrypt/keys", help="Keys storage.") + help=config_help("backup_dir")) + add("--key-dir", default="/etc/letsencrypt/keys", + help=config_help("key_dir")) add("--cert-dir", default="/etc/letsencrypt/certs", - help="Certificates storage.") + help=config_help("cert_dir")) add("--le-vhost-ext", default="-le-ssl.conf", - help="SSL vhost configuration extension.") + help=config_help("le_vhost_ext")) add("--cert-path", default="/etc/letsencrypt/certs/cert-letsencrypt.pem", - help="Let's Encrypt certificate file.") + help=config_help("cert_path")) add("--chain-path", default="/etc/letsencrypt/certs/chain-letsencrypt.pem", - help="Let's Encrypt chain file.") + help=config_help("chain_path")) - add("--apache-ctl", default="apache2ctl", - help="Path to the 'apache2ctl' binary, used for 'configtest' and " - "retrieving Apache2 version number.") - add("--apache-enmod", default="a2enmod", - help="Path to the Apache 'a2enmod' binary.") - add("--apache-init-script", default="/etc/init.d/apache2", - help="Path to the Apache init script (used for server reload/restart).") add("--apache-server-root", default="/etc/apache2", - help="Apache server root directory.") + help=config_help("apache_server_root")) add("--apache-mod-ssl-conf", default="/etc/letsencrypt/options-ssl.conf", - help="Contains standard Apache SSL directives.") + help=config_help("apache_mod_ssl_conf")) + add("--apache-ctl", default="apache2ctl", help=config_help("apache_ctl")) + add("--apache-enmod", default="a2enmod", help=config_help("apache_enmod")) + add("--apache-init-script", default="/etc/init.d/apache2", + help=config_help("apache_init_script")) return parser @@ -102,26 +98,26 @@ def main(): # pylint: disable=too-many-branches # Set up logging logger = logging.getLogger() logger.setLevel(logging.INFO) - if config.use_curses: + if args.use_curses: logger.addHandler(log.DialogHandler()) displayer = display.NcursesDisplay() else: displayer = display.FileDisplay(sys.stdout) zope.component.provideUtility(displayer) - if config.view_config_changes: + if args.view_config_changes: client.view_config_changes(config) sys.exit() - if config.revoke: + if args.revoke: client.revoke(config) sys.exit() - if config.rollback > 0: - client.rollback(config) + if args.rollback > 0: + client.rollback(args.rollback, config) sys.exit() - if not config.eula: + if not args.eula: display_eula() # Make sure we actually get an installer that is functioning properly @@ -140,14 +136,14 @@ def main(): # pylint: disable=too-many-branches else: auth = client.determine_authenticator(config) - if config.domains is None: + if args.domains is None: domains = choose_names(installer) # Prepare for init of Client - if config.privkey is None: - privkey = client.init_key(config.rsa_key_size, config.key_dir) + if args.privkey is None: + privkey = client.init_key(args.rsa_key_size, config.key_dir) else: - privkey = client.Client.Key(config.privkey[0], config.privkey[1]) + privkey = client.Client.Key(args.privkey[0], args.privkey[1]) acme = client.Client(config, privkey, auth, installer) @@ -163,7 +159,7 @@ def main(): # pylint: disable=too-many-branches if installer is not None and cert_file is not None: acme.deploy_certificate(domains, privkey, cert_file, chain_file) if installer is not None: - acme.enhance_config(domains, config.redirect) + acme.enhance_config(domains, args.redirect) def display_eula():