Merge pull request #5228 from jonasbn/master

Documentation update to certbot/main.py
This commit is contained in:
Noah Swartz
2017-12-06 16:26:26 -08:00
committed by GitHub
+377 -33
View File
@@ -43,7 +43,15 @@ logger = logging.getLogger(__name__)
def _suggest_donation_if_appropriate(config): def _suggest_donation_if_appropriate(config):
"""Potentially suggest a donation to support Certbot.""" """Potentially suggest a donation to support Certbot.
:param config: Configuration object
:type config: interfaces.IConfig
:returns: `None`
:rtype: None
"""
assert config.verb != "renew" assert config.verb != "renew"
if config.staging: if config.staging:
# --dry-run implies --staging # --dry-run implies --staging
@@ -55,6 +63,15 @@ def _suggest_donation_if_appropriate(config):
reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) reporter_util.add_message(msg, reporter_util.LOW_PRIORITY)
def _report_successful_dry_run(config): def _report_successful_dry_run(config):
"""Reports on successful dry run
:param config: Configuration object
:type config: interfaces.IConfig
:returns: `None`
:rtype: None
"""
reporter_util = zope.component.getUtility(interfaces.IReporter) reporter_util = zope.component.getUtility(interfaces.IReporter)
assert config.verb != "renew" assert config.verb != "renew"
reporter_util.add_message("The dry run was successful.", reporter_util.add_message("The dry run was successful.",
@@ -68,8 +85,23 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N
then performs that action. Includes calls to hooks, various reports, then performs that action. Includes calls to hooks, various reports,
checks, and requests for user input. checks, and requests for user input.
:param config: Configuration object
:type config: interfaces.IConfig
:param domains: List of domain names to get a certificate. Defaults to `None`
:type domains: `list` of `str`
:param certname: Name of new certificate. Defaults to `None`
:type certname: str
:param lineage: Certificate lineage object. Defaults to `None`
:type lineage: storage.RenewableCert
:returns: the issued certificate or `None` if doing a dry run :returns: the issued certificate or `None` if doing a dry run
:rtype: `storage.RenewableCert` or `None` :rtype: storage.RenewableCert or None
:raises errors.Error: if certificate could not be obtained
""" """
hooks.pre_hook(config) hooks.pre_hook(config)
try: try:
@@ -96,11 +128,18 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N
def _handle_subset_cert_request(config, domains, cert): def _handle_subset_cert_request(config, domains, cert):
"""Figure out what to do if a previous cert had a subset of the names now requested """Figure out what to do if a previous cert had a subset of the names now requested
:param storage.RenewableCert cert: :param config: Configuration object
:type config: interfaces.IConfig
:param domains: List of domain names
:type domains: `list` of `str`
:param cert: Certificate object
:type cert: storage.RenewableCert
:returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname :returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname
action can be: "newcert" | "renew" | "reinstall" action can be: "newcert" | "renew" | "reinstall"
:rtype: tuple :rtype: `tuple` of `str`
""" """
existing = ", ".join(cert.names()) existing = ", ".join(cert.names())
@@ -137,11 +176,15 @@ def _handle_subset_cert_request(config, domains, cert):
def _handle_identical_cert_request(config, lineage): def _handle_identical_cert_request(config, lineage):
"""Figure out what to do if a lineage has the same names as a previously obtained one """Figure out what to do if a lineage has the same names as a previously obtained one
:param storage.RenewableCert lineage: :param config: Configuration object
:type config: interfaces.IConfig
:param lineage: Certificate lineage object
:type lineage: storage.RenewableCert
:returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname :returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname
action can be: "newcert" | "renew" | "reinstall" action can be: "newcert" | "renew" | "reinstall"
:rtype: tuple :rtype: `tuple` of `str`
""" """
if not lineage.ensure_deployed(): if not lineage.ensure_deployed():
@@ -186,11 +229,18 @@ def _find_lineage_for_domains(config, domains):
the client run if the user chooses to cancel the operation when the client run if the user chooses to cancel the operation when
prompted). prompted).
:param config: Configuration object
:type config: interfaces.IConfig
:param domains: List of domain names
:type domains: `list` of `str`
:returns: Two-element tuple containing desired new-certificate behavior as :returns: Two-element tuple containing desired new-certificate behavior as
a string token ("reinstall", "renew", or "newcert"), plus either a string token ("reinstall", "renew", or "newcert"), plus either
a RenewableCert instance or None if renewal shouldn't occur. a RenewableCert instance or `None` if renewal shouldn't occur.
:rtype: `tuple` of `str` and :class:`storage.RenewableCert` or `None`
:raises .Error: If the user would like to rerun the client again. :raises errors.Error: If the user would like to rerun the client again.
""" """
# Considering the possibility that the requested certificate is # Considering the possibility that the requested certificate is
@@ -214,9 +264,20 @@ def _find_lineage_for_domains(config, domains):
def _find_cert(config, domains, certname): def _find_cert(config, domains, certname):
"""Finds an existing certificate object given domains and/or a certificate name. """Finds an existing certificate object given domains and/or a certificate name.
:param config: Configuration object
:type config: interfaces.IConfig
:param domains: List of domain names
:type domains: `list` of `str`
:param certname: Name of certificate
:type certname: str
:returns: Two-element tuple of a boolean that indicates if this function should be :returns: Two-element tuple of a boolean that indicates if this function should be
followed by a call to fetch a certificate from the server, and either a followed by a call to fetch a certificate from the server, and either a
RenewableCert instance or None. RenewableCert instance or None.
:rtype: `tuple` of `bool` and :class:`storage.RenewableCert` or `None`
""" """
action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname)
if action == "reinstall": if action == "reinstall":
@@ -226,11 +287,22 @@ def _find_cert(config, domains, certname):
def _find_lineage_for_domains_and_certname(config, domains, certname): def _find_lineage_for_domains_and_certname(config, domains, certname):
"""Find appropriate lineage based on given domains and/or certname. """Find appropriate lineage based on given domains and/or certname.
:param config: Configuration object
:type config: interfaces.IConfig
:param domains: List of domain names
:type domains: `list` of `str`
:param certname: Name of certificate
:type certname: str
:returns: Two-element tuple containing desired new-certificate behavior as :returns: Two-element tuple containing desired new-certificate behavior as
a string token ("reinstall", "renew", or "newcert"), plus either a string token ("reinstall", "renew", or "newcert"), plus either
a RenewableCert instance or None if renewal shouldn't occur. a RenewableCert instance or None if renewal should not occur.
:raises .Error: If the user would like to rerun the client again. :rtype: `tuple` of `str` and :class:`storage.RenewableCert` or `None`
:raises errors.Error: If the user would like to rerun the client again.
""" """
if not certname: if not certname:
@@ -255,6 +327,24 @@ def _find_lineage_for_domains_and_certname(config, domains, certname):
def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains):
"""Ask user to confirm update cert certname to contain new_domains. """Ask user to confirm update cert certname to contain new_domains.
:param config: Configuration object
:type config: interfaces.IConfig
:param new_domains: List of new domain names
:type new_domains: `list` of `str`
:param certname: Name of certificate
:type certname: str
:param old_domains: List of old domain names
:type old_domains: `list` of `str`
:returns: None
:rtype: None
:raises errors.ConfigurationError: if cert name and domains mismatch
""" """
if config.renew_with_new_domains: if config.renew_with_new_domains:
return return
@@ -272,6 +362,19 @@ def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains):
def _find_domains_or_certname(config, installer): def _find_domains_or_certname(config, installer):
"""Retrieve domains and certname from config or user input. """Retrieve domains and certname from config or user input.
:param config: Configuration object
:type config: interfaces.IConfig
:param installer: Installer object
:type installer: interfaces.IInstaller
:returns: Two-part tuple of domains and certname
:rtype: `tuple` of list of `str` and `str`
:raises errors.Error: Usage message, if parameters are not used correctly
""" """
domains = None domains = None
certname = config.certname certname = config.certname
@@ -299,9 +402,17 @@ def _find_domains_or_certname(config, installer):
def _report_new_cert(config, cert_path, fullchain_path, key_path=None): def _report_new_cert(config, cert_path, fullchain_path, key_path=None):
"""Reports the creation of a new certificate to the user. """Reports the creation of a new certificate to the user.
:param str cert_path: path to cert :param cert_path: path to certificate
:param str fullchain_path: path to full chain :type cert_path: str
:param str key_path: path to private key, if available
:param fullchain_path: path to full chain
:type fullchain_path: str
:param key_path: path to private key, if available
:type key_path: str
:returns: `None`
:rtype: None
""" """
if config.dry_run: if config.dry_run:
@@ -337,14 +448,14 @@ def _determine_account(config):
if ``config.account`` is ``None``, it will be updated based on the if ``config.account`` is ``None``, it will be updated based on the
user input. Same for ``config.email``. user input. Same for ``config.email``.
:param argparse.Namespace config: CLI arguments :param config: Configuration object
:param certbot.interface.IConfig config: Configuration object :type config: interfaces.IConfig
:param .AccountStorage account_storage: Account storage.
:returns: Account and optionally ACME client API (biproduct of new :returns: Account and optionally ACME client API (biproduct of new
registration). registration).
:rtype: `tuple` of `certbot.account.Account` and :rtype: tuple of :class:`certbot.account.Account` and :class:`acme.client.Client`
`acme.client.Client`
:raises errors.Error: If unable to register an account with ACME server
""" """
account_storage = account.AccountFileStorage(config) account_storage = account.AccountFileStorage(config)
@@ -392,9 +503,13 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b
deleting happens automatically, unless if both `--cert-name` and `--cert-path` were deleting happens automatically, unless if both `--cert-name` and `--cert-path` were
specified with conflicting values. specified with conflicting values.
:param `configuration.NamespaceConfig` config: parsed command line arguments :param config: parsed command line arguments
:type config: interfaces.IConfig
:raises `error.Errors`: If anything goes wrong, including bad user input, if an overlapping :returns: `None`
:rtype: None
:raises errors.Error: If anything goes wrong, including bad user input, if an overlapping
archive dir is found for the specified lineage, etc ... archive dir is found for the specified lineage, etc ...
""" """
display = zope.component.getUtility(interfaces.IDisplay) display = zope.component.getUtility(interfaces.IDisplay)
@@ -474,6 +589,20 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b
def _init_le_client(config, authenticator, installer): def _init_le_client(config, authenticator, installer):
"""Initialize Let's Encrypt Client
:param config: Configuration object
:type config: interfaces.IConfig
:param authenticator: Acme authentication handler
:type authenticator: interfaces.IAuthenticator
:param installer: Installer object
:type installer: interfaces.IInstaller
:returns: client: Client object
:rtype: client.Client
"""
if authenticator is not None: if authenticator is not None:
# if authenticator was given, then we will need account... # if authenticator was given, then we will need account...
acc, acme = _determine_account(config) acc, acme = _determine_account(config)
@@ -487,7 +616,18 @@ def _init_le_client(config, authenticator, installer):
def unregister(config, unused_plugins): def unregister(config, unused_plugins):
"""Deactivate account on server""" """Deactivate account on server
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
account_storage = account.AccountFileStorage(config) account_storage = account.AccountFileStorage(config)
accounts = account_storage.find_all() accounts = account_storage.find_all()
reporter_util = zope.component.getUtility(interfaces.IReporter) reporter_util = zope.component.getUtility(interfaces.IReporter)
@@ -516,8 +656,18 @@ def unregister(config, unused_plugins):
def register(config, unused_plugins): def register(config, unused_plugins):
"""Create or modify accounts on the server.""" """Create or modify accounts on the server.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None` or a string indicating and error
:rtype: None or str
"""
# Portion of _determine_account logic to see whether accounts already # Portion of _determine_account logic to see whether accounts already
# exist or not. # exist or not.
account_storage = account.AccountFileStorage(config) account_storage = account.AccountFileStorage(config)
@@ -558,6 +708,24 @@ def register(config, unused_plugins):
add_msg("Your e-mail address was updated to {0}.".format(config.email)) add_msg("Your e-mail address was updated to {0}.".format(config.email))
def _install_cert(config, le_client, domains, lineage=None): def _install_cert(config, le_client, domains, lineage=None):
"""Install a cert
:param config: Configuration object
:type config: interfaces.IConfig
:param le_client: Client object
:type le_client: client.Client
:param plugins: List of domains
:type plugins: `list` of `str`
:param lineage: Certificate lineage object. Defaults to `None`
:type lineage: storage.RenewableCert
:returns: `None`
:rtype: None
"""
path_provider = lineage if lineage else config path_provider = lineage if lineage else config
assert path_provider.cert_path is not None assert path_provider.cert_path is not None
@@ -566,7 +734,18 @@ def _install_cert(config, le_client, domains, lineage=None):
le_client.enhance_config(domains, path_provider.chain_path) le_client.enhance_config(domains, path_provider.chain_path)
def install(config, plugins): def install(config, plugins):
"""Install a previously obtained cert in a server.""" """Install a previously obtained cert in a server.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
# XXX: Update for renewer/RenewableCert # XXX: Update for renewer/RenewableCert
# FIXME: be consistent about whether errors are raised or returned from # FIXME: be consistent about whether errors are raised or returned from
# this function ... # this function ...
@@ -582,7 +761,18 @@ def install(config, plugins):
def plugins_cmd(config, plugins): def plugins_cmd(config, plugins):
"""List server software plugins.""" """List server software plugins.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
logger.debug("Expected interfaces: %s", config.ifaces) logger.debug("Expected interfaces: %s", config.ifaces)
ifaces = [] if config.ifaces is None else config.ifaces ifaces = [] if config.ifaces is None else config.ifaces
@@ -610,7 +800,18 @@ def plugins_cmd(config, plugins):
def rollback(config, plugins): def rollback(config, plugins):
"""Rollback server configuration changes made during install.""" """Rollback server configuration changes made during install.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
client.rollback(config.installer, config.checkpoints, config, plugins) client.rollback(config.installer, config.checkpoints, config, plugins)
@@ -619,6 +820,15 @@ def config_changes(config, unused_plugins):
View checkpoints and associated configuration changes. View checkpoints and associated configuration changes.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
""" """
client.view_config_changes(config, num=config.num) client.view_config_changes(config, num=config.num)
@@ -627,6 +837,16 @@ def update_symlinks(config, unused_plugins):
Use the information in the config file to make symlinks point to Use the information in the config file to make symlinks point to
the correct archive directory. the correct archive directory.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
""" """
cert_manager.update_live_symlinks(config) cert_manager.update_live_symlinks(config)
@@ -635,6 +855,16 @@ def rename(config, unused_plugins):
Use the information in the config file to rename an existing Use the information in the config file to rename an existing
lineage. lineage.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
""" """
cert_manager.rename_lineage(config) cert_manager.rename_lineage(config)
@@ -643,16 +873,47 @@ def delete(config, unused_plugins):
Use the information in the config file to delete an existing Use the information in the config file to delete an existing
lineage. lineage.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
""" """
cert_manager.delete(config) cert_manager.delete(config)
def certificates(config, unused_plugins): def certificates(config, unused_plugins):
"""Display information about certs configured with Certbot """Display information about certs configured with Certbot
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
""" """
cert_manager.certificates(config) cert_manager.certificates(config)
def revoke(config, unused_plugins): # TODO: coop with renewal config def revoke(config, unused_plugins): # TODO: coop with renewal config
"""Revoke a previously obtained certificate.""" """Revoke a previously obtained certificate.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None` or string indicating error in case of error
:rtype: None or str
"""
# For user-agent construction # For user-agent construction
config.installer = config.authenticator = "None" config.installer = config.authenticator = "None"
if config.key_path is not None: # revocation by cert key if config.key_path is not None: # revocation by cert key
@@ -678,7 +939,18 @@ def revoke(config, unused_plugins): # TODO: coop with renewal config
def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals
"""Obtain a certificate and install.""" """Obtain a certificate and install.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
# TODO: Make run as close to auth + install as possible # TODO: Make run as close to auth + install as possible
# Possible difficulties: config.csr was hacked into auth # Possible difficulties: config.csr was hacked into auth
try: try:
@@ -718,6 +990,16 @@ def _csr_get_and_save_cert(config, le_client):
This works differently in the CSR case (for now) because we don't This works differently in the CSR case (for now) because we don't
have the privkey, and therefore can't construct the files for a lineage. have the privkey, and therefore can't construct the files for a lineage.
So we just save the cert & chain to disk :/ So we just save the cert & chain to disk :/
:param config: Configuration object
:type config: interfaces.IConfig
:param client: Client object
:type client: client.Client
:returns: `cert_path` and `fullchain_path` as absolute paths to the actual files
:rtype: `tuple` of `str`
""" """
csr, _ = config.actual_csr csr, _ = config.actual_csr
certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr) certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr)
@@ -730,7 +1012,23 @@ def _csr_get_and_save_cert(config, le_client):
return cert_path, fullchain_path return cert_path, fullchain_path
def renew_cert(config, plugins, lineage): def renew_cert(config, plugins, lineage):
"""Renew & save an existing cert. Do not install it.""" """Renew & save an existing cert. Do not install it.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:param lineage: Certificate lineage object
:type lineage: storage.RenewableCert
:returns: `None`
:rtype: None
:raises errors.PluginSelectionError: MissingCommandlineFlag if supplied parameters do not pass
"""
try: try:
# installers are used in auth mode to determine domain names # installers are used in auth mode to determine domain names
installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly") installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly")
@@ -757,8 +1055,20 @@ def renew_cert(config, plugins, lineage):
def certonly(config, plugins): def certonly(config, plugins):
"""Authenticate & obtain cert, but do not install it. """Authenticate & obtain cert, but do not install it.
This implements the 'certonly' subcommand.""" This implements the 'certonly' subcommand.
:param config: Configuration object
:type config: interfaces.IConfig
:param plugins: List of plugins
:type plugins: `list` of `str`
:returns: `None`
:rtype: None
:raises errors.Error: If specified plugin could not be used
"""
# SETUP: Select plugins and construct a client instance # SETUP: Select plugins and construct a client instance
try: try:
# installers are used in auth mode to determine domain names # installers are used in auth mode to determine domain names
@@ -792,7 +1102,18 @@ def certonly(config, plugins):
_suggest_donation_if_appropriate(config) _suggest_donation_if_appropriate(config)
def renew(config, unused_plugins): def renew(config, unused_plugins):
"""Renew previously-obtained certificates.""" """Renew previously-obtained certificates.
:param config: Configuration object
:type config: interfaces.IConfig
:param unused_plugins: List of plugins (deprecated)
:type unused_plugins: `list` of `str`
:returns: `None`
:rtype: None
"""
try: try:
renewal.handle_renewal_request(config) renewal.handle_renewal_request(config)
finally: finally:
@@ -800,7 +1121,15 @@ def renew(config, unused_plugins):
def make_or_verify_needed_dirs(config): def make_or_verify_needed_dirs(config):
"""Create or verify existence of config, work, and hook directories.""" """Create or verify existence of config, work, and hook directories.
:param config: Configuration object
:type config: interfaces.IConfig
:returns: `None`
:rtype: None
"""
util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE, util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE,
os.geteuid(), config.strict_permissions) os.geteuid(), config.strict_permissions)
util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE, util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE,
@@ -816,7 +1145,15 @@ def make_or_verify_needed_dirs(config):
def set_displayer(config): def set_displayer(config):
"""Set the displayer""" """Set the displayer
:param config: Configuration object
:type config: interfaces.IConfig
:returns: `None`
:rtype: None
"""
if config.quiet: if config.quiet:
config.noninteractive_mode = True config.noninteractive_mode = True
displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w")) displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w"))
@@ -829,7 +1166,14 @@ def set_displayer(config):
def main(cli_args=sys.argv[1:]): def main(cli_args=sys.argv[1:]):
"""Command line argument parsing and main script execution.""" """Command line argument parsing and main script execution.
:returns: result of requested command
:raises errors.Error: OS errors triggered by wrong permissions
:raises errors.Error: error if plugin command is not supported
"""
log.pre_arg_parse_setup() log.pre_arg_parse_setup()
plugins = plugins_disco.PluginsRegistry.find_all() plugins = plugins_disco.PluginsRegistry.find_all()