mirror of
https://github.com/certbot/certbot.git
synced 2026-08-02 08:03:19 +02:00
Implement delete command (#3913)
* organize cert_manager.py * add delete files to cert manager and storage * add tests * add to main and cli * Clean up all related files we can find, even if some are missing. * error messages, debug logs, and remove RenewerConfiguration * add logs for failure to remove * remove renewer_config_file
This commit is contained in:
+112
-93
@@ -6,10 +6,8 @@ import pytz
|
|||||||
import traceback
|
import traceback
|
||||||
import zope.component
|
import zope.component
|
||||||
|
|
||||||
from certbot import configuration
|
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
from certbot import interfaces
|
from certbot import interfaces
|
||||||
from certbot import renewal
|
|
||||||
from certbot import storage
|
from certbot import storage
|
||||||
from certbot import util
|
from certbot import util
|
||||||
|
|
||||||
@@ -17,6 +15,10 @@ from certbot.display import util as display_util
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Commands
|
||||||
|
###################
|
||||||
|
|
||||||
def update_live_symlinks(config):
|
def update_live_symlinks(config):
|
||||||
"""Update the certificate file family symlinks to use archive_dir.
|
"""Update the certificate file family symlinks to use archive_dir.
|
||||||
|
|
||||||
@@ -26,36 +28,22 @@ def update_live_symlinks(config):
|
|||||||
.. note:: This assumes that the installation is using a Reverter object.
|
.. note:: This assumes that the installation is using a Reverter object.
|
||||||
|
|
||||||
:param config: Configuration.
|
:param config: Configuration.
|
||||||
:type config: :class:`certbot.interfaces.IConfig`
|
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
renewer_config = configuration.RenewerConfiguration(config)
|
for renewal_file in storage.renewal_conf_files(config):
|
||||||
for renewal_file in renewal.renewal_conf_files(renewer_config):
|
storage.RenewableCert(renewal_file, config, update_symlinks=True)
|
||||||
storage.RenewableCert(renewal_file,
|
|
||||||
configuration.RenewerConfiguration(renewer_config),
|
|
||||||
update_symlinks=True)
|
|
||||||
|
|
||||||
def rename_lineage(config):
|
def rename_lineage(config):
|
||||||
"""Rename the specified lineage to the new name.
|
"""Rename the specified lineage to the new name.
|
||||||
|
|
||||||
:param config: Configuration.
|
:param config: Configuration.
|
||||||
:type config: :class:`certbot.interfaces.IConfig`
|
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||||
renewer_config = configuration.RenewerConfiguration(config)
|
|
||||||
|
|
||||||
certname = config.certname
|
certname = _get_certname(config, "rename")
|
||||||
if not certname:
|
|
||||||
filenames = renewal.renewal_conf_files(renewer_config)
|
|
||||||
choices = [storage.lineagename_for_filename(name) for name in filenames]
|
|
||||||
if not choices:
|
|
||||||
raise errors.Error("No existing certificates found.")
|
|
||||||
code, index = disp.menu("Which certificate would you like to rename?",
|
|
||||||
choices, ok_label="Select", flag="--cert-name")
|
|
||||||
if code != display_util.OK or not index in range(0, len(choices)):
|
|
||||||
raise errors.Error("User ended interaction.")
|
|
||||||
certname = choices[index]
|
|
||||||
|
|
||||||
new_certname = config.new_certname
|
new_certname = config.new_certname
|
||||||
if not new_certname:
|
if not new_certname:
|
||||||
@@ -68,10 +56,110 @@ def rename_lineage(config):
|
|||||||
if not lineage:
|
if not lineage:
|
||||||
raise errors.ConfigurationError("No existing certificate with name "
|
raise errors.ConfigurationError("No existing certificate with name "
|
||||||
"{0} found.".format(certname))
|
"{0} found.".format(certname))
|
||||||
storage.rename_renewal_config(certname, new_certname, renewer_config)
|
storage.rename_renewal_config(certname, new_certname, config)
|
||||||
disp.notification("Successfully renamed {0} to {1}."
|
disp.notification("Successfully renamed {0} to {1}."
|
||||||
.format(certname, new_certname), pause=False)
|
.format(certname, new_certname), pause=False)
|
||||||
|
|
||||||
|
def certificates(config):
|
||||||
|
"""Display information about certs configured with Certbot
|
||||||
|
|
||||||
|
:param config: Configuration.
|
||||||
|
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||||
|
"""
|
||||||
|
parsed_certs = []
|
||||||
|
parse_failures = []
|
||||||
|
for renewal_file in storage.renewal_conf_files(config):
|
||||||
|
try:
|
||||||
|
renewal_candidate = storage.RenewableCert(renewal_file, config)
|
||||||
|
parsed_certs.append(renewal_candidate)
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
logger.warning("Renewal configuration file %s produced an "
|
||||||
|
"unexpected error: %s. Skipping.", renewal_file, e)
|
||||||
|
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||||
|
parse_failures.append(renewal_file)
|
||||||
|
|
||||||
|
# Describe all the certs
|
||||||
|
_describe_certs(parsed_certs, parse_failures)
|
||||||
|
|
||||||
|
def delete(config):
|
||||||
|
"""Delete Certbot files associated with a certificate lineage."""
|
||||||
|
certname = _get_certname(config, "delete")
|
||||||
|
storage.delete_files(config, certname)
|
||||||
|
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||||
|
disp.notification("Deleted all files relating to certificate {0}."
|
||||||
|
.format(certname), pause=False)
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Public Helpers
|
||||||
|
###################
|
||||||
|
|
||||||
|
def lineage_for_certname(config, certname):
|
||||||
|
"""Find a lineage object with name certname."""
|
||||||
|
def update_cert_for_name_match(candidate_lineage, rv):
|
||||||
|
"""Return cert if it has name certname, else return rv
|
||||||
|
"""
|
||||||
|
matching_lineage_name_cert = rv
|
||||||
|
if candidate_lineage.lineagename == certname:
|
||||||
|
matching_lineage_name_cert = candidate_lineage
|
||||||
|
return matching_lineage_name_cert
|
||||||
|
return _search_lineages(config, update_cert_for_name_match, None)
|
||||||
|
|
||||||
|
def domains_for_certname(config, certname):
|
||||||
|
"""Find the domains in the cert with name certname."""
|
||||||
|
def update_domains_for_name_match(candidate_lineage, rv):
|
||||||
|
"""Return domains if certname matches, else return rv
|
||||||
|
"""
|
||||||
|
matching_domains = rv
|
||||||
|
if candidate_lineage.lineagename == certname:
|
||||||
|
matching_domains = candidate_lineage.names()
|
||||||
|
return matching_domains
|
||||||
|
return _search_lineages(config, update_domains_for_name_match, None)
|
||||||
|
|
||||||
|
def find_duplicative_certs(config, domains):
|
||||||
|
"""Find existing certs that duplicate the request."""
|
||||||
|
def update_certs_for_domain_matches(candidate_lineage, rv):
|
||||||
|
"""Return cert as identical_names_cert if it matches,
|
||||||
|
or subset_names_cert if it matches as subset
|
||||||
|
"""
|
||||||
|
# TODO: Handle these differently depending on whether they are
|
||||||
|
# expired or still valid?
|
||||||
|
identical_names_cert, subset_names_cert = rv
|
||||||
|
candidate_names = set(candidate_lineage.names())
|
||||||
|
if candidate_names == set(domains):
|
||||||
|
identical_names_cert = candidate_lineage
|
||||||
|
elif candidate_names.issubset(set(domains)):
|
||||||
|
# This logic finds and returns the largest subset-names cert
|
||||||
|
# in the case where there are several available.
|
||||||
|
if subset_names_cert is None:
|
||||||
|
subset_names_cert = candidate_lineage
|
||||||
|
elif len(candidate_names) > len(subset_names_cert.names()):
|
||||||
|
subset_names_cert = candidate_lineage
|
||||||
|
return (identical_names_cert, subset_names_cert)
|
||||||
|
|
||||||
|
return _search_lineages(config, update_certs_for_domain_matches, (None, None))
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Private Helpers
|
||||||
|
###################
|
||||||
|
|
||||||
|
def _get_certname(config, verb):
|
||||||
|
"""Get certname from flag, interactively, or error out.
|
||||||
|
"""
|
||||||
|
certname = config.certname
|
||||||
|
if not certname:
|
||||||
|
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||||
|
filenames = storage.renewal_conf_files(config)
|
||||||
|
choices = [storage.lineagename_for_filename(name) for name in filenames]
|
||||||
|
if not choices:
|
||||||
|
raise errors.Error("No existing certificates found.")
|
||||||
|
code, index = disp.menu("Which certificate would you like to {0}?".format(verb),
|
||||||
|
choices, ok_label="Select", flag="--cert-name")
|
||||||
|
if code != display_util.OK or not index in range(0, len(choices)):
|
||||||
|
raise errors.Error("User ended interaction.")
|
||||||
|
certname = choices[index]
|
||||||
|
return certname
|
||||||
|
|
||||||
def _report_lines(msgs):
|
def _report_lines(msgs):
|
||||||
"""Format a results report for a category of single-line renewal outcomes"""
|
"""Format a results report for a category of single-line renewal outcomes"""
|
||||||
return " " + "\n ".join(str(msg) for msg in msgs)
|
return " " + "\n ".join(str(msg) for msg in msgs)
|
||||||
@@ -126,42 +214,18 @@ def _describe_certs(parsed_certs, parse_failures):
|
|||||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||||
disp.notification("\n".join(out), pause=False, wrap=False)
|
disp.notification("\n".join(out), pause=False, wrap=False)
|
||||||
|
|
||||||
def certificates(config):
|
def _search_lineages(cli_config, func, initial_rv):
|
||||||
"""Display information about certs configured with Certbot
|
|
||||||
|
|
||||||
:param config: Configuration.
|
|
||||||
:type config: :class:`certbot.interfaces.IConfig`
|
|
||||||
"""
|
|
||||||
renewer_config = configuration.RenewerConfiguration(config)
|
|
||||||
parsed_certs = []
|
|
||||||
parse_failures = []
|
|
||||||
for renewal_file in renewal.renewal_conf_files(renewer_config):
|
|
||||||
try:
|
|
||||||
renewal_candidate = storage.RenewableCert(renewal_file,
|
|
||||||
configuration.RenewerConfiguration(config))
|
|
||||||
parsed_certs.append(renewal_candidate)
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
logger.warning("Renewal configuration file %s produced an "
|
|
||||||
"unexpected error: %s. Skipping.", renewal_file, e)
|
|
||||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
|
||||||
parse_failures.append(renewal_file)
|
|
||||||
|
|
||||||
# Describe all the certs
|
|
||||||
_describe_certs(parsed_certs, parse_failures)
|
|
||||||
|
|
||||||
def _search_lineages(config, func, initial_rv):
|
|
||||||
"""Iterate func over unbroken lineages, allowing custom return conditions.
|
"""Iterate func over unbroken lineages, allowing custom return conditions.
|
||||||
|
|
||||||
Allows flexible customization of return values, including multiple
|
Allows flexible customization of return values, including multiple
|
||||||
return values and complex checks.
|
return values and complex checks.
|
||||||
"""
|
"""
|
||||||
cli_config = configuration.RenewerConfiguration(config)
|
|
||||||
configs_dir = cli_config.renewal_configs_dir
|
configs_dir = cli_config.renewal_configs_dir
|
||||||
# Verify the directory is there
|
# Verify the directory is there
|
||||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
||||||
|
|
||||||
rv = initial_rv
|
rv = initial_rv
|
||||||
for renewal_file in renewal.renewal_conf_files(cli_config):
|
for renewal_file in storage.renewal_conf_files(cli_config):
|
||||||
try:
|
try:
|
||||||
candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
|
candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
|
||||||
except (errors.CertStorageError, IOError):
|
except (errors.CertStorageError, IOError):
|
||||||
@@ -170,48 +234,3 @@ def _search_lineages(config, func, initial_rv):
|
|||||||
continue
|
continue
|
||||||
rv = func(candidate_lineage, rv)
|
rv = func(candidate_lineage, rv)
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def lineage_for_certname(config, certname):
|
|
||||||
"""Find a lineage object with name certname."""
|
|
||||||
def update_cert_for_name_match(candidate_lineage, rv):
|
|
||||||
"""Return cert if it has name certname, else return rv
|
|
||||||
"""
|
|
||||||
matching_lineage_name_cert = rv
|
|
||||||
if candidate_lineage.lineagename == certname:
|
|
||||||
matching_lineage_name_cert = candidate_lineage
|
|
||||||
return matching_lineage_name_cert
|
|
||||||
return _search_lineages(config, update_cert_for_name_match, None)
|
|
||||||
|
|
||||||
def domains_for_certname(config, certname):
|
|
||||||
"""Find the domains in the cert with name certname."""
|
|
||||||
def update_domains_for_name_match(candidate_lineage, rv):
|
|
||||||
"""Return domains if certname matches, else return rv
|
|
||||||
"""
|
|
||||||
matching_domains = rv
|
|
||||||
if candidate_lineage.lineagename == certname:
|
|
||||||
matching_domains = candidate_lineage.names()
|
|
||||||
return matching_domains
|
|
||||||
return _search_lineages(config, update_domains_for_name_match, None)
|
|
||||||
|
|
||||||
def find_duplicative_certs(config, domains):
|
|
||||||
"""Find existing certs that duplicate the request."""
|
|
||||||
def update_certs_for_domain_matches(candidate_lineage, rv):
|
|
||||||
"""Return cert as identical_names_cert if it matches,
|
|
||||||
or subset_names_cert if it matches as subset
|
|
||||||
"""
|
|
||||||
# TODO: Handle these differently depending on whether they are
|
|
||||||
# expired or still valid?
|
|
||||||
identical_names_cert, subset_names_cert = rv
|
|
||||||
candidate_names = set(candidate_lineage.names())
|
|
||||||
if candidate_names == set(domains):
|
|
||||||
identical_names_cert = candidate_lineage
|
|
||||||
elif candidate_names.issubset(set(domains)):
|
|
||||||
# This logic finds and returns the largest subset-names cert
|
|
||||||
# in the case where there are several available.
|
|
||||||
if subset_names_cert is None:
|
|
||||||
subset_names_cert = candidate_lineage
|
|
||||||
elif len(candidate_names) > len(subset_names_cert.names()):
|
|
||||||
subset_names_cert = candidate_lineage
|
|
||||||
return (identical_names_cert, subset_names_cert)
|
|
||||||
|
|
||||||
return _search_lineages(config, update_certs_for_domain_matches, (None, None))
|
|
||||||
|
|||||||
+11
-6
@@ -82,6 +82,7 @@ manage certificates:
|
|||||||
certificates Display information about certs you have from Certbot
|
certificates Display information about certs you have from Certbot
|
||||||
revoke Revoke a certificate (supply --cert-path)
|
revoke Revoke a certificate (supply --cert-path)
|
||||||
rename Rename a certificate
|
rename Rename a certificate
|
||||||
|
delete Delete a certificate
|
||||||
|
|
||||||
manage your account with Let's Encrypt:
|
manage your account with Let's Encrypt:
|
||||||
register Create a Let's Encrypt ACME account
|
register Create a Let's Encrypt ACME account
|
||||||
@@ -352,13 +353,17 @@ VERB_HELP = [
|
|||||||
"short": "List all certificates managed by Certbot",
|
"short": "List all certificates managed by Certbot",
|
||||||
"opts": "List all certificates managed by Certbot"
|
"opts": "List all certificates managed by Certbot"
|
||||||
}),
|
}),
|
||||||
|
("delete", {
|
||||||
|
"short": "Clean up all files related to a certificate",
|
||||||
|
"opts": "Options for deleting a certificate"
|
||||||
|
}),
|
||||||
("revoke", {
|
("revoke", {
|
||||||
"short": "Revoke a certificate specified with --cert-path",
|
"short": "Revoke a certificate specified with --cert-path",
|
||||||
"opts": "Options for revocation of certs"
|
"opts": "Options for revocation of certs"
|
||||||
}),
|
}),
|
||||||
("rename", {
|
("rename", {
|
||||||
"short": "Change a certificate's name (for management purposes)",
|
"short": "Change a certificate's name (for management purposes)",
|
||||||
"opts": "Options changing certificate names"
|
"opts": "Options for changing certificate names"
|
||||||
}),
|
}),
|
||||||
("register", {
|
("register", {
|
||||||
"short": "Register for account with Let's Encrypt / other ACME server",
|
"short": "Register for account with Let's Encrypt / other ACME server",
|
||||||
@@ -410,7 +415,8 @@ class HelpfulArgumentParser(object):
|
|||||||
"register": main.register, "renew": main.renew,
|
"register": main.register, "renew": main.renew,
|
||||||
"revoke": main.revoke, "rollback": main.rollback,
|
"revoke": main.revoke, "rollback": main.rollback,
|
||||||
"everything": main.run, "update_symlinks": main.update_symlinks,
|
"everything": main.run, "update_symlinks": main.update_symlinks,
|
||||||
"certificates": main.certificates, "rename": main.rename}
|
"certificates": main.certificates, "rename": main.rename,
|
||||||
|
"delete": main.delete}
|
||||||
|
|
||||||
# List of topics for which additional help can be provided
|
# List of topics for which additional help can be provided
|
||||||
HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + list(self.VERBS)
|
HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + list(self.VERBS)
|
||||||
@@ -763,7 +769,7 @@ def _add_all_groups(helpful):
|
|||||||
helpful.add_group("paths", description="Arguments changing execution paths & servers")
|
helpful.add_group("paths", description="Arguments changing execution paths & servers")
|
||||||
helpful.add_group("manage",
|
helpful.add_group("manage",
|
||||||
description="Various subcommands and flags are available for managing your certificates:",
|
description="Various subcommands and flags are available for managing your certificates:",
|
||||||
verbs=["certificates", "renew", "revoke", "rename"])
|
verbs=["certificates", "delete", "renew", "revoke", "rename"])
|
||||||
|
|
||||||
# VERBS
|
# VERBS
|
||||||
for verb, docs in VERB_HELP:
|
for verb, docs in VERB_HELP:
|
||||||
@@ -810,13 +816,12 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
|||||||
"multiple -d flags or enter a comma separated list of domains "
|
"multiple -d flags or enter a comma separated list of domains "
|
||||||
"as a parameter. (default: Ask)")
|
"as a parameter. (default: Ask)")
|
||||||
helpful.add(
|
helpful.add(
|
||||||
[None, "run", "certonly", "manage"],
|
[None, "run", "certonly", "manage", "rename", "delete"],
|
||||||
"--cert-name", dest="certname",
|
"--cert-name", dest="certname",
|
||||||
metavar="CERTNAME", default=None,
|
metavar="CERTNAME", default=None,
|
||||||
help="Certificate name to apply. Only one certificate name can be used "
|
help="Certificate name to apply. Only one certificate name can be used "
|
||||||
"per Certbot run. To see certificate names, run 'certbot certificates'. "
|
"per Certbot run. To see certificate names, run 'certbot certificates'. "
|
||||||
"If there is no existing certificate with this name and "
|
"When creating a new certificate, specifies the new certificate's name.")
|
||||||
"domains are requested, create a new certificate with this name.")
|
|
||||||
helpful.add(
|
helpful.add(
|
||||||
["rename", "manage"],
|
["rename", "manage"],
|
||||||
"--updated-cert-name", dest="new_certname",
|
"--updated-cert-name", dest="new_certname",
|
||||||
|
|||||||
+1
-2
@@ -15,7 +15,6 @@ import certbot
|
|||||||
|
|
||||||
from certbot import account
|
from certbot import account
|
||||||
from certbot import auth_handler
|
from certbot import auth_handler
|
||||||
from certbot import configuration
|
|
||||||
from certbot import constants
|
from certbot import constants
|
||||||
from certbot import crypto_util
|
from certbot import crypto_util
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
@@ -307,7 +306,7 @@ class Client(object):
|
|||||||
new_name, OpenSSL.crypto.dump_certificate(
|
new_name, OpenSSL.crypto.dump_certificate(
|
||||||
OpenSSL.crypto.FILETYPE_PEM, certr.body.wrapped),
|
OpenSSL.crypto.FILETYPE_PEM, certr.body.wrapped),
|
||||||
key.pem, crypto_util.dump_pyopenssl_chain(chain),
|
key.pem, crypto_util.dump_pyopenssl_chain(chain),
|
||||||
configuration.RenewerConfiguration(self.config.namespace))
|
self.config)
|
||||||
|
|
||||||
def save_certificate(self, certr, chain_cert,
|
def save_certificate(self, certr, chain_cert,
|
||||||
cert_path, chain_path, fullchain_path):
|
cert_path, chain_path, fullchain_path):
|
||||||
|
|||||||
@@ -25,9 +25,16 @@ class NamespaceConfig(object):
|
|||||||
- `csr_dir`
|
- `csr_dir`
|
||||||
- `in_progress_dir`
|
- `in_progress_dir`
|
||||||
- `key_dir`
|
- `key_dir`
|
||||||
- `renewer_config_file`
|
|
||||||
- `temp_checkpoint_dir`
|
- `temp_checkpoint_dir`
|
||||||
|
|
||||||
|
And the following paths are dynamically resolved using
|
||||||
|
:attr:`~certbot.interfaces.IConfig.config_dir` and relative
|
||||||
|
paths defined in :py:mod:`certbot.constants`:
|
||||||
|
|
||||||
|
- `default_archive_dir`
|
||||||
|
- `live_dir`
|
||||||
|
- `renewal_configs_dir`
|
||||||
|
|
||||||
:ivar namespace: Namespace typically produced by
|
:ivar namespace: Namespace typically produced by
|
||||||
:meth:`argparse.ArgumentParser.parse_args`.
|
:meth:`argparse.ArgumentParser.parse_args`.
|
||||||
:type namespace: :class:`argparse.Namespace`
|
:type namespace: :class:`argparse.Namespace`
|
||||||
@@ -85,16 +92,6 @@ class NamespaceConfig(object):
|
|||||||
new_ns = copy.deepcopy(self.namespace)
|
new_ns = copy.deepcopy(self.namespace)
|
||||||
return type(self)(new_ns)
|
return type(self)(new_ns)
|
||||||
|
|
||||||
|
|
||||||
class RenewerConfiguration(object):
|
|
||||||
"""Configuration wrapper for renewer."""
|
|
||||||
|
|
||||||
def __init__(self, namespace):
|
|
||||||
self.namespace = namespace
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return getattr(self.namespace, name)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_archive_dir(self): # pylint: disable=missing-docstring
|
def default_archive_dir(self): # pylint: disable=missing-docstring
|
||||||
return os.path.join(self.namespace.config_dir, constants.ARCHIVE_DIR)
|
return os.path.join(self.namespace.config_dir, constants.ARCHIVE_DIR)
|
||||||
@@ -108,11 +105,6 @@ class RenewerConfiguration(object):
|
|||||||
return os.path.join(
|
return os.path.join(
|
||||||
self.namespace.config_dir, constants.RENEWAL_CONFIGS_DIR)
|
self.namespace.config_dir, constants.RENEWAL_CONFIGS_DIR)
|
||||||
|
|
||||||
@property
|
|
||||||
def renewer_config_file(self): # pylint: disable=missing-docstring
|
|
||||||
return os.path.join(
|
|
||||||
self.namespace.config_dir, constants.RENEWER_CONFIG_FILENAME)
|
|
||||||
|
|
||||||
|
|
||||||
def check_config_sanity(config):
|
def check_config_sanity(config):
|
||||||
"""Validate command line options and display error message if
|
"""Validate command line options and display error message if
|
||||||
|
|||||||
@@ -92,6 +92,3 @@ TEMP_CHECKPOINT_DIR = "temp_checkpoint"
|
|||||||
|
|
||||||
RENEWAL_CONFIGS_DIR = "renewal"
|
RENEWAL_CONFIGS_DIR = "renewal"
|
||||||
"""Renewal configs directory, relative to `IConfig.config_dir`."""
|
"""Renewal configs directory, relative to `IConfig.config_dir`."""
|
||||||
|
|
||||||
RENEWER_CONFIG_FILENAME = "renewer.conf"
|
|
||||||
"""Renewer config file name (relative to `IConfig.config_dir`)."""
|
|
||||||
|
|||||||
@@ -223,9 +223,6 @@ class IConfig(zope.interface.Interface):
|
|||||||
temp_checkpoint_dir = zope.interface.Attribute(
|
temp_checkpoint_dir = zope.interface.Attribute(
|
||||||
"Temporary checkpoint directory.")
|
"Temporary checkpoint directory.")
|
||||||
|
|
||||||
renewer_config_file = zope.interface.Attribute(
|
|
||||||
"Location of renewal configuration file.")
|
|
||||||
|
|
||||||
no_verify_ssl = zope.interface.Attribute(
|
no_verify_ssl = zope.interface.Attribute(
|
||||||
"Disable verification of the ACME server's certificate.")
|
"Disable verification of the ACME server's certificate.")
|
||||||
tls_sni_01_port = zope.interface.Attribute(
|
tls_sni_01_port = zope.interface.Attribute(
|
||||||
|
|||||||
@@ -511,6 +511,14 @@ def rename(config, unused_plugins):
|
|||||||
"""
|
"""
|
||||||
cert_manager.rename_lineage(config)
|
cert_manager.rename_lineage(config)
|
||||||
|
|
||||||
|
def delete(config, unused_plugins):
|
||||||
|
"""Delete a certificate
|
||||||
|
|
||||||
|
Use the information in the config file to delete an existing
|
||||||
|
lineage.
|
||||||
|
"""
|
||||||
|
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
|
||||||
"""
|
"""
|
||||||
|
|||||||
+4
-21
@@ -1,7 +1,6 @@
|
|||||||
"""Functionality for autorenewal and associated juggling of configurations"""
|
"""Functionality for autorenewal and associated juggling of configurations"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import copy
|
import copy
|
||||||
import glob
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
@@ -11,7 +10,6 @@ import zope.component
|
|||||||
|
|
||||||
import OpenSSL
|
import OpenSSL
|
||||||
|
|
||||||
from certbot import configuration
|
|
||||||
from certbot import cli
|
from certbot import cli
|
||||||
|
|
||||||
from certbot import crypto_util
|
from certbot import crypto_util
|
||||||
@@ -34,17 +32,6 @@ STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent",
|
|||||||
INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"]
|
INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"]
|
||||||
|
|
||||||
|
|
||||||
def renewal_conf_files(config):
|
|
||||||
"""Return /path/to/*.conf in the renewal conf directory"""
|
|
||||||
return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf"))
|
|
||||||
|
|
||||||
def renewal_file_for_certname(config, certname):
|
|
||||||
"""Return /path/to/certname.conf in the renewal conf directory"""
|
|
||||||
path = os.path.join(config.renewal_configs_dir, "{0}.conf".format(certname))
|
|
||||||
if not os.path.exists(path):
|
|
||||||
raise errors.CertStorageError("No certificate found with name {0}.".format(certname))
|
|
||||||
return path
|
|
||||||
|
|
||||||
def _reconstitute(config, full_path):
|
def _reconstitute(config, full_path):
|
||||||
"""Try to instantiate a RenewableCert, updating config with relevant items.
|
"""Try to instantiate a RenewableCert, updating config with relevant items.
|
||||||
|
|
||||||
@@ -63,8 +50,7 @@ def _reconstitute(config, full_path):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
renewal_candidate = storage.RenewableCert(
|
renewal_candidate = storage.RenewableCert(full_path, config)
|
||||||
full_path, configuration.RenewerConfiguration(config))
|
|
||||||
except (errors.CertStorageError, IOError) as exc:
|
except (errors.CertStorageError, IOError) as exc:
|
||||||
logger.warning(exc)
|
logger.warning(exc)
|
||||||
logger.warning("Renewal configuration file %s is broken. Skipping.", full_path)
|
logger.warning("Renewal configuration file %s is broken. Skipping.", full_path)
|
||||||
@@ -246,9 +232,8 @@ def renew_cert(config, le_client, lineage):
|
|||||||
new_cert = OpenSSL.crypto.dump_certificate(
|
new_cert = OpenSSL.crypto.dump_certificate(
|
||||||
OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped)
|
OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped)
|
||||||
new_chain = crypto_util.dump_pyopenssl_chain(new_chain)
|
new_chain = crypto_util.dump_pyopenssl_chain(new_chain)
|
||||||
renewal_conf = configuration.RenewerConfiguration(config.namespace)
|
|
||||||
# TODO: Check return value of save_successor
|
# TODO: Check return value of save_successor
|
||||||
lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf)
|
lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, config)
|
||||||
lineage.update_all_links_to(lineage.latest_common_version())
|
lineage.update_all_links_to(lineage.latest_common_version())
|
||||||
|
|
||||||
hooks.renew_hook(config, lineage.names(), lineage.live_dir)
|
hooks.renew_hook(config, lineage.names(), lineage.live_dir)
|
||||||
@@ -317,12 +302,10 @@ def handle_renewal_request(config):
|
|||||||
"command. The renew verb may provide other options "
|
"command. The renew verb may provide other options "
|
||||||
"for selecting certificates to renew in the future.")
|
"for selecting certificates to renew in the future.")
|
||||||
|
|
||||||
renewer_config = configuration.RenewerConfiguration(config)
|
|
||||||
|
|
||||||
if config.certname:
|
if config.certname:
|
||||||
conf_files = [renewal_file_for_certname(renewer_config, config.certname)]
|
conf_files = [storage.renewal_file_for_certname(config, config.certname)]
|
||||||
else:
|
else:
|
||||||
conf_files = renewal_conf_files(renewer_config)
|
conf_files = storage.renewal_conf_files(config)
|
||||||
|
|
||||||
renew_successes = []
|
renew_successes = []
|
||||||
renew_failures = []
|
renew_failures = []
|
||||||
|
|||||||
+117
-23
@@ -1,5 +1,6 @@
|
|||||||
"""Renewable certificates storage."""
|
"""Renewable certificates storage."""
|
||||||
import datetime
|
import datetime
|
||||||
|
import glob
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -7,6 +8,7 @@ import re
|
|||||||
import configobj
|
import configobj
|
||||||
import parsedatetime
|
import parsedatetime
|
||||||
import pytz
|
import pytz
|
||||||
|
import shutil
|
||||||
import six
|
import six
|
||||||
|
|
||||||
import certbot
|
import certbot
|
||||||
@@ -20,9 +22,22 @@ from certbot import util
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
|
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
|
||||||
|
README = "README"
|
||||||
CURRENT_VERSION = util.get_strict_version(certbot.__version__)
|
CURRENT_VERSION = util.get_strict_version(certbot.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
def renewal_conf_files(config):
|
||||||
|
"""Return /path/to/*.conf in the renewal conf directory"""
|
||||||
|
return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf"))
|
||||||
|
|
||||||
|
def renewal_file_for_certname(config, certname):
|
||||||
|
"""Return /path/to/certname.conf in the renewal conf directory"""
|
||||||
|
path = os.path.join(config.renewal_configs_dir, "{0}.conf".format(certname))
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise errors.CertStorageError("No certificate found with name {0} (expected "
|
||||||
|
"{1}).".format(certname, path))
|
||||||
|
return path
|
||||||
|
|
||||||
def config_with_defaults(config=None):
|
def config_with_defaults(config=None):
|
||||||
"""Merge supplied config, if provided, on top of builtin defaults."""
|
"""Merge supplied config, if provided, on top of builtin defaults."""
|
||||||
defaults_copy = configobj.ConfigObj(constants.RENEWER_DEFAULTS)
|
defaults_copy = configobj.ConfigObj(constants.RENEWER_DEFAULTS)
|
||||||
@@ -99,13 +114,11 @@ def write_renewal_config(o_filename, n_filename, archive_dir, target, relevant_d
|
|||||||
def rename_renewal_config(prev_name, new_name, cli_config):
|
def rename_renewal_config(prev_name, new_name, cli_config):
|
||||||
"""Renames cli_config.certname's config to cli_config.new_certname.
|
"""Renames cli_config.certname's config to cli_config.new_certname.
|
||||||
|
|
||||||
:param .RenewerConfiguration cli_config: parsed command line
|
:param .NamespaceConfig cli_config: parsed command line
|
||||||
arguments
|
arguments
|
||||||
"""
|
"""
|
||||||
prev_filename = os.path.join(
|
prev_filename = renewal_filename_for_lineagename(cli_config, prev_name)
|
||||||
cli_config.renewal_configs_dir, prev_name) + ".conf"
|
new_filename = renewal_filename_for_lineagename(cli_config, new_name)
|
||||||
new_filename = os.path.join(
|
|
||||||
cli_config.renewal_configs_dir, new_name) + ".conf"
|
|
||||||
if os.path.exists(new_filename):
|
if os.path.exists(new_filename):
|
||||||
raise errors.ConfigurationError("The new certificate name "
|
raise errors.ConfigurationError("The new certificate name "
|
||||||
"is already in use.")
|
"is already in use.")
|
||||||
@@ -122,15 +135,14 @@ def update_configuration(lineagename, archive_dir, target, cli_config):
|
|||||||
:param str lineagename: Name of the lineage being modified
|
:param str lineagename: Name of the lineage being modified
|
||||||
:param str archive_dir: Absolute path to the archive directory
|
:param str archive_dir: Absolute path to the archive directory
|
||||||
:param dict target: Maps ALL_FOUR to their symlink paths
|
:param dict target: Maps ALL_FOUR to their symlink paths
|
||||||
:param .RenewerConfiguration cli_config: parsed command line
|
:param .NamespaceConfig cli_config: parsed command line
|
||||||
arguments
|
arguments
|
||||||
|
|
||||||
:returns: Configuration object for the updated config file
|
:returns: Configuration object for the updated config file
|
||||||
:rtype: configobj.ConfigObj
|
:rtype: configobj.ConfigObj
|
||||||
|
|
||||||
"""
|
"""
|
||||||
config_filename = os.path.join(
|
config_filename = renewal_filename_for_lineagename(cli_config, lineagename)
|
||||||
cli_config.renewal_configs_dir, lineagename) + ".conf"
|
|
||||||
temp_filename = config_filename + ".new"
|
temp_filename = config_filename + ".new"
|
||||||
|
|
||||||
# If an existing tempfile exists, delete it
|
# If an existing tempfile exists, delete it
|
||||||
@@ -198,10 +210,98 @@ def lineagename_for_filename(config_filename):
|
|||||||
"renewal config file name must end in .conf")
|
"renewal config file name must end in .conf")
|
||||||
return os.path.basename(config_filename[:-len(".conf")])
|
return os.path.basename(config_filename[:-len(".conf")])
|
||||||
|
|
||||||
|
def renewal_filename_for_lineagename(config, lineagename):
|
||||||
|
"""Returns the lineagename for a configuration filename.
|
||||||
|
"""
|
||||||
|
return os.path.join(config.renewal_configs_dir, lineagename) + ".conf"
|
||||||
|
|
||||||
def _relpath_from_file(archive_dir, from_file):
|
def _relpath_from_file(archive_dir, from_file):
|
||||||
"""Path to a directory from a file"""
|
"""Path to a directory from a file"""
|
||||||
return os.path.relpath(archive_dir, os.path.dirname(from_file))
|
return os.path.relpath(archive_dir, os.path.dirname(from_file))
|
||||||
|
|
||||||
|
def _full_archive_path(config_obj, cli_config, lineagename):
|
||||||
|
"""Returns the full archive path for a lineagename
|
||||||
|
|
||||||
|
Uses cli_config to determine archive path if not available from config_obj.
|
||||||
|
|
||||||
|
:param configobj.ConfigObj config_obj: Renewal conf file contents (can be None)
|
||||||
|
:param configuration.NamespaceConfig cli_config: Main config file
|
||||||
|
:param str lineagename: Certificate name
|
||||||
|
"""
|
||||||
|
if config_obj and "archive_dir" in config_obj:
|
||||||
|
return config_obj["archive_dir"]
|
||||||
|
else:
|
||||||
|
return os.path.join(cli_config.default_archive_dir, lineagename)
|
||||||
|
|
||||||
|
def _full_live_path(cli_config, lineagename):
|
||||||
|
"""Returns the full default live path for a lineagename"""
|
||||||
|
return os.path.join(cli_config.live_dir, lineagename)
|
||||||
|
|
||||||
|
def delete_files(config, certname):
|
||||||
|
"""Delete all files related to the certificate.
|
||||||
|
|
||||||
|
If some files are not found, ignore them and continue.
|
||||||
|
"""
|
||||||
|
renewal_filename = renewal_file_for_certname(config, certname)
|
||||||
|
# file exists
|
||||||
|
full_default_archive_dir = _full_archive_path(None, config, certname)
|
||||||
|
full_default_live_dir = _full_live_path(config, certname)
|
||||||
|
try:
|
||||||
|
renewal_config = configobj.ConfigObj(renewal_filename)
|
||||||
|
except configobj.ConfigObjError:
|
||||||
|
# config is corrupted
|
||||||
|
logger.warning("Could not parse %s. You may wish to manually "
|
||||||
|
"delete the contents of %s and %s.", renewal_filename,
|
||||||
|
full_default_live_dir, full_default_archive_dir)
|
||||||
|
raise errors.CertStorageError(
|
||||||
|
"error parsing {0}".format(renewal_filename))
|
||||||
|
finally:
|
||||||
|
# we couldn't read it, but let's at least delete it
|
||||||
|
# if this was going to fail, it already would have.
|
||||||
|
os.remove(renewal_filename)
|
||||||
|
logger.debug("Removed %s", renewal_filename)
|
||||||
|
|
||||||
|
# cert files and (hopefully) live directory
|
||||||
|
# it's not guaranteed that the files are in our default storage
|
||||||
|
# structure. so, first delete the cert files.
|
||||||
|
directory_names = set()
|
||||||
|
for kind in ALL_FOUR:
|
||||||
|
link = renewal_config.get(kind)
|
||||||
|
try:
|
||||||
|
os.remove(link)
|
||||||
|
logger.debug("Removed %s", link)
|
||||||
|
except OSError:
|
||||||
|
logger.debug("Unable to delete %s", link)
|
||||||
|
directory = os.path.dirname(link)
|
||||||
|
directory_names.add(directory)
|
||||||
|
|
||||||
|
# if all four were in the same directory, and the only thing left
|
||||||
|
# is the README file (or nothing), delete that directory.
|
||||||
|
# this will be wrong in very few but some cases.
|
||||||
|
if len(directory_names) == 1:
|
||||||
|
# delete the README file
|
||||||
|
directory = directory_names.pop()
|
||||||
|
readme_path = os.path.join(directory, README)
|
||||||
|
try:
|
||||||
|
os.remove(readme_path)
|
||||||
|
logger.debug("Removed %s", readme_path)
|
||||||
|
except OSError:
|
||||||
|
logger.debug("Unable to delete %s", readme_path)
|
||||||
|
# if it's now empty, delete the directory
|
||||||
|
try:
|
||||||
|
os.rmdir(directory) # only removes empty directories
|
||||||
|
logger.debug("Removed %s", directory)
|
||||||
|
except OSError:
|
||||||
|
logger.debug("Unable to remove %s; may not be empty.", directory)
|
||||||
|
|
||||||
|
# archive directory
|
||||||
|
try:
|
||||||
|
archive_path = _full_archive_path(renewal_config, config, certname)
|
||||||
|
shutil.rmtree(archive_path)
|
||||||
|
logger.debug("Removed %s", archive_path)
|
||||||
|
except OSError:
|
||||||
|
logger.debug("Unable to remove %s", archive_path)
|
||||||
|
|
||||||
|
|
||||||
class RenewableCert(object):
|
class RenewableCert(object):
|
||||||
# pylint: disable=too-many-instance-attributes,too-many-public-methods
|
# pylint: disable=too-many-instance-attributes,too-many-public-methods
|
||||||
@@ -244,7 +344,7 @@ class RenewableCert(object):
|
|||||||
|
|
||||||
:param str config_filename: the path to the renewal config file
|
:param str config_filename: the path to the renewal config file
|
||||||
that defines this lineage.
|
that defines this lineage.
|
||||||
:param .RenewerConfiguration: parsed command line arguments
|
:param .NamespaceConfig: parsed command line arguments
|
||||||
|
|
||||||
:raises .CertStorageError: if the configuration file's name didn't end
|
:raises .CertStorageError: if the configuration file's name didn't end
|
||||||
in ".conf", or the file is missing or broken.
|
in ".conf", or the file is missing or broken.
|
||||||
@@ -303,11 +403,8 @@ class RenewableCert(object):
|
|||||||
@property
|
@property
|
||||||
def archive_dir(self):
|
def archive_dir(self):
|
||||||
"""Returns the default or specified archive directory"""
|
"""Returns the default or specified archive directory"""
|
||||||
if "archive_dir" in self.configuration:
|
return _full_archive_path(self.configuration,
|
||||||
return self.configuration["archive_dir"]
|
self.cli_config, self.lineagename)
|
||||||
else:
|
|
||||||
return os.path.join(
|
|
||||||
self.cli_config.default_archive_dir, self.lineagename)
|
|
||||||
|
|
||||||
def relative_archive_dir(self, from_file):
|
def relative_archive_dir(self, from_file):
|
||||||
"""Returns the default or specified archive directory as a relative path
|
"""Returns the default or specified archive directory as a relative path
|
||||||
@@ -827,7 +924,7 @@ class RenewableCert(object):
|
|||||||
:param str cert: the initial certificate version in PEM format
|
:param str cert: the initial certificate version in PEM format
|
||||||
:param str privkey: the private key in PEM format
|
:param str privkey: the private key in PEM format
|
||||||
:param str chain: the certificate chain in PEM format
|
:param str chain: the certificate chain in PEM format
|
||||||
:param .RenewerConfiguration cli_config: parsed command line
|
:param .NamespaceConfig cli_config: parsed command line
|
||||||
arguments
|
arguments
|
||||||
|
|
||||||
:returns: the newly-created RenewalCert object
|
:returns: the newly-created RenewalCert object
|
||||||
@@ -843,16 +940,13 @@ class RenewableCert(object):
|
|||||||
logger.debug("Creating directory %s.", i)
|
logger.debug("Creating directory %s.", i)
|
||||||
config_file, config_filename = util.unique_lineage_name(
|
config_file, config_filename = util.unique_lineage_name(
|
||||||
cli_config.renewal_configs_dir, lineagename)
|
cli_config.renewal_configs_dir, lineagename)
|
||||||
if not config_filename.endswith(".conf"):
|
|
||||||
raise errors.CertStorageError(
|
|
||||||
"renewal config file name must end in .conf")
|
|
||||||
|
|
||||||
# Determine where on disk everything will go
|
# Determine where on disk everything will go
|
||||||
# lineagename will now potentially be modified based on which
|
# lineagename will now potentially be modified based on which
|
||||||
# renewal configuration file could actually be created
|
# renewal configuration file could actually be created
|
||||||
lineagename = os.path.basename(config_filename)[:-len(".conf")]
|
lineagename = lineagename_for_filename(config_filename)
|
||||||
archive = os.path.join(cli_config.default_archive_dir, lineagename)
|
archive = _full_archive_path(None, cli_config, lineagename)
|
||||||
live_dir = os.path.join(cli_config.live_dir, lineagename)
|
live_dir = _full_live_path(cli_config, lineagename)
|
||||||
if os.path.exists(archive):
|
if os.path.exists(archive):
|
||||||
raise errors.CertStorageError(
|
raise errors.CertStorageError(
|
||||||
"archive directory exists for " + lineagename)
|
"archive directory exists for " + lineagename)
|
||||||
@@ -887,7 +981,7 @@ class RenewableCert(object):
|
|||||||
f.write(cert + chain)
|
f.write(cert + chain)
|
||||||
|
|
||||||
# Write a README file to the live directory
|
# Write a README file to the live directory
|
||||||
readme_path = os.path.join(live_dir, "README")
|
readme_path = os.path.join(live_dir, README)
|
||||||
with open(readme_path, "w") as f:
|
with open(readme_path, "w") as f:
|
||||||
logger.debug("Writing README to %s.", readme_path)
|
logger.debug("Writing README to %s.", readme_path)
|
||||||
f.write("This directory contains your keys and certificates.\n\n"
|
f.write("This directory contains your keys and certificates.\n\n"
|
||||||
@@ -928,7 +1022,7 @@ class RenewableCert(object):
|
|||||||
:param str new_privkey: the new private key, in PEM format,
|
:param str new_privkey: the new private key, in PEM format,
|
||||||
or ``None``, if the private key has not changed
|
or ``None``, if the private key has not changed
|
||||||
:param str new_chain: the new chain, in PEM format
|
:param str new_chain: the new chain, in PEM format
|
||||||
:param .RenewerConfiguration cli_config: parsed command line
|
:param .NamespaceConfig cli_config: parsed command line
|
||||||
arguments
|
arguments
|
||||||
|
|
||||||
:returns: the new version number that was created
|
:returns: the new version number that was created
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ class BaseCertManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
os.makedirs(os.path.join(self.tempdir, "renewal"))
|
os.makedirs(os.path.join(self.tempdir, "renewal"))
|
||||||
|
|
||||||
self.cli_config = mock.MagicMock(
|
self.cli_config = configuration.NamespaceConfig(mock.MagicMock(
|
||||||
config_dir=self.tempdir,
|
config_dir=self.tempdir,
|
||||||
work_dir=self.tempdir,
|
work_dir=self.tempdir,
|
||||||
logs_dir=self.tempdir,
|
logs_dir=self.tempdir,
|
||||||
quiet=False,
|
quiet=False,
|
||||||
)
|
))
|
||||||
|
|
||||||
self.domains = {
|
self.domains = {
|
||||||
"example.org": None,
|
"example.org": None,
|
||||||
@@ -47,7 +47,7 @@ class BaseCertManagerTest(unittest.TestCase):
|
|||||||
junk.close()
|
junk.close()
|
||||||
|
|
||||||
def _set_up_config(self, domain, custom_archive):
|
def _set_up_config(self, domain, custom_archive):
|
||||||
# TODO: maybe provide RenewerConfiguration.make_dirs?
|
# TODO: maybe provide NamespaceConfig.make_dirs?
|
||||||
# TODO: main() should create those dirs, c.f. #902
|
# TODO: main() should create those dirs, c.f. #902
|
||||||
os.makedirs(os.path.join(self.tempdir, "live", domain))
|
os.makedirs(os.path.join(self.tempdir, "live", domain))
|
||||||
config = configobj.ConfigObj()
|
config = configobj.ConfigObj()
|
||||||
@@ -111,6 +111,21 @@ class UpdateLiveSymlinksTest(BaseCertManagerTest):
|
|||||||
os.chdir(prev_dir)
|
os.chdir(prev_dir)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteTest(storage_test.BaseRenewableCertTest):
|
||||||
|
"""Tests for certbot.cert_manager.delete
|
||||||
|
"""
|
||||||
|
@mock.patch('zope.component.getUtility')
|
||||||
|
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||||
|
@mock.patch('certbot.storage.delete_files')
|
||||||
|
def test_delete(self, mock_delete_files, mock_lineage_for_certname, unused_get_utility):
|
||||||
|
"""Test delete"""
|
||||||
|
mock_lineage_for_certname.return_value = self.test_rc
|
||||||
|
self.cli_config.certname = "example.org"
|
||||||
|
from certbot import cert_manager
|
||||||
|
cert_manager.delete(self.cli_config)
|
||||||
|
self.assertTrue(mock_delete_files.called)
|
||||||
|
|
||||||
|
|
||||||
class CertificatesTest(BaseCertManagerTest):
|
class CertificatesTest(BaseCertManagerTest):
|
||||||
"""Tests for certbot.cert_manager.certificates
|
"""Tests for certbot.cert_manager.certificates
|
||||||
"""
|
"""
|
||||||
@@ -151,12 +166,12 @@ class CertificatesTest(BaseCertManagerTest):
|
|||||||
def test_certificates_no_files(self, mock_utility, mock_logger):
|
def test_certificates_no_files(self, mock_utility, mock_logger):
|
||||||
tempdir = tempfile.mkdtemp()
|
tempdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
cli_config = mock.MagicMock(
|
cli_config = configuration.NamespaceConfig(mock.MagicMock(
|
||||||
config_dir=tempdir,
|
config_dir=tempdir,
|
||||||
work_dir=tempdir,
|
work_dir=tempdir,
|
||||||
logs_dir=tempdir,
|
logs_dir=tempdir,
|
||||||
quiet=False,
|
quiet=False,
|
||||||
)
|
))
|
||||||
|
|
||||||
os.makedirs(os.path.join(tempdir, "renewal"))
|
os.makedirs(os.path.join(tempdir, "renewal"))
|
||||||
self._certificates(cli_config)
|
self._certificates(cli_config)
|
||||||
@@ -202,90 +217,85 @@ class CertificatesTest(BaseCertManagerTest):
|
|||||||
self.assertTrue('INVALID: TEST CERT' in out)
|
self.assertTrue('INVALID: TEST CERT' in out)
|
||||||
|
|
||||||
|
|
||||||
class SearchLineagesTest(unittest.TestCase):
|
class SearchLineagesTest(BaseCertManagerTest):
|
||||||
"""Tests for certbot.cert_manager._search_lineages."""
|
"""Tests for certbot.cert_manager._search_lineages."""
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.RenewerConfiguration')
|
|
||||||
@mock.patch('certbot.util.make_or_verify_dir')
|
@mock.patch('certbot.util.make_or_verify_dir')
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.storage.RenewableCert')
|
@mock.patch('certbot.storage.RenewableCert')
|
||||||
def test_cert_storage_error(self, mock_renewable_cert, mock_renewal_conf_files,
|
def test_cert_storage_error(self, mock_renewable_cert, mock_renewal_conf_files,
|
||||||
mock_make_or_verify_dir, mock_renewer_config):
|
mock_make_or_verify_dir):
|
||||||
mock_renewal_conf_files.return_value = ["badfile"]
|
mock_renewal_conf_files.return_value = ["badfile"]
|
||||||
mock_renewable_cert.side_effect = errors.CertStorageError
|
mock_renewable_cert.side_effect = errors.CertStorageError
|
||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.assertEqual(cert_manager._search_lineages(None, lambda x: x, "check"), "check")
|
self.assertEqual(cert_manager._search_lineages(self.cli_config, lambda x: x, "check"),
|
||||||
|
"check")
|
||||||
self.assertTrue(mock_make_or_verify_dir.called)
|
self.assertTrue(mock_make_or_verify_dir.called)
|
||||||
self.assertTrue(mock_renewer_config)
|
|
||||||
|
|
||||||
|
|
||||||
class LineageForCertnameTest(unittest.TestCase):
|
class LineageForCertnameTest(BaseCertManagerTest):
|
||||||
"""Tests for certbot.cert_manager.lineage_for_certname"""
|
"""Tests for certbot.cert_manager.lineage_for_certname"""
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.RenewerConfiguration')
|
|
||||||
@mock.patch('certbot.util.make_or_verify_dir')
|
@mock.patch('certbot.util.make_or_verify_dir')
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.storage.RenewableCert')
|
@mock.patch('certbot.storage.RenewableCert')
|
||||||
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
||||||
mock_make_or_verify_dir, mock_renewer_config):
|
mock_make_or_verify_dir):
|
||||||
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
||||||
mock_match = mock.Mock(lineagename="example.com")
|
mock_match = mock.Mock(lineagename="example.com")
|
||||||
mock_renewable_cert.return_value = mock_match
|
mock_renewable_cert.return_value = mock_match
|
||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
self.assertEqual(cert_manager.lineage_for_certname(None, "example.com"), mock_match)
|
self.assertEqual(cert_manager.lineage_for_certname(self.cli_config, "example.com"),
|
||||||
|
mock_match)
|
||||||
self.assertTrue(mock_make_or_verify_dir.called)
|
self.assertTrue(mock_make_or_verify_dir.called)
|
||||||
self.assertTrue(mock_renewer_config)
|
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.RenewerConfiguration')
|
|
||||||
@mock.patch('certbot.util.make_or_verify_dir')
|
@mock.patch('certbot.util.make_or_verify_dir')
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.storage.RenewableCert')
|
@mock.patch('certbot.storage.RenewableCert')
|
||||||
def test_no_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
def test_no_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
||||||
mock_make_or_verify_dir, mock_renewer_config):
|
mock_make_or_verify_dir):
|
||||||
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
||||||
mock_match = mock.Mock(lineagename="other.com")
|
mock_match = mock.Mock(lineagename="other.com")
|
||||||
mock_renewable_cert.return_value = mock_match
|
mock_renewable_cert.return_value = mock_match
|
||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
self.assertEqual(cert_manager.lineage_for_certname(None, "example.com"), None)
|
self.assertEqual(cert_manager.lineage_for_certname(self.cli_config, "example.com"),
|
||||||
|
None)
|
||||||
self.assertTrue(mock_make_or_verify_dir.called)
|
self.assertTrue(mock_make_or_verify_dir.called)
|
||||||
self.assertTrue(mock_renewer_config)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainsForCertnameTest(unittest.TestCase):
|
class DomainsForCertnameTest(BaseCertManagerTest):
|
||||||
"""Tests for certbot.cert_manager.domains_for_certname"""
|
"""Tests for certbot.cert_manager.domains_for_certname"""
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.RenewerConfiguration')
|
|
||||||
@mock.patch('certbot.util.make_or_verify_dir')
|
@mock.patch('certbot.util.make_or_verify_dir')
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.storage.RenewableCert')
|
@mock.patch('certbot.storage.RenewableCert')
|
||||||
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
||||||
mock_make_or_verify_dir, mock_renewer_config):
|
mock_make_or_verify_dir):
|
||||||
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
||||||
mock_match = mock.Mock(lineagename="example.com")
|
mock_match = mock.Mock(lineagename="example.com")
|
||||||
domains = ["example.com", "example.org"]
|
domains = ["example.com", "example.org"]
|
||||||
mock_match.names.return_value = domains
|
mock_match.names.return_value = domains
|
||||||
mock_renewable_cert.return_value = mock_match
|
mock_renewable_cert.return_value = mock_match
|
||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
self.assertEqual(cert_manager.domains_for_certname(None, "example.com"), domains)
|
self.assertEqual(cert_manager.domains_for_certname(self.cli_config, "example.com"),
|
||||||
|
domains)
|
||||||
self.assertTrue(mock_make_or_verify_dir.called)
|
self.assertTrue(mock_make_or_verify_dir.called)
|
||||||
self.assertTrue(mock_renewer_config)
|
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.RenewerConfiguration')
|
|
||||||
@mock.patch('certbot.util.make_or_verify_dir')
|
@mock.patch('certbot.util.make_or_verify_dir')
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.storage.RenewableCert')
|
@mock.patch('certbot.storage.RenewableCert')
|
||||||
def test_no_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
def test_no_match(self, mock_renewable_cert, mock_renewal_conf_files,
|
||||||
mock_make_or_verify_dir, mock_renewer_config):
|
mock_make_or_verify_dir):
|
||||||
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
mock_renewal_conf_files.return_value = ["somefile.conf"]
|
||||||
mock_match = mock.Mock(lineagename="example.com")
|
mock_match = mock.Mock(lineagename="example.com")
|
||||||
domains = ["example.com", "example.org"]
|
domains = ["example.com", "example.org"]
|
||||||
mock_match.names.return_value = domains
|
mock_match.names.return_value = domains
|
||||||
mock_renewable_cert.return_value = mock_match
|
mock_renewable_cert.return_value = mock_match
|
||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
self.assertEqual(cert_manager.domains_for_certname(None, "other.com"), None)
|
self.assertEqual(cert_manager.domains_for_certname(self.cli_config, "other.com"),
|
||||||
|
None)
|
||||||
self.assertTrue(mock_make_or_verify_dir.called)
|
self.assertTrue(mock_make_or_verify_dir.called)
|
||||||
self.assertTrue(mock_renewer_config)
|
|
||||||
|
|
||||||
|
|
||||||
class RenameLineageTest(BaseCertManagerTest):
|
class RenameLineageTest(BaseCertManagerTest):
|
||||||
@@ -293,7 +303,7 @@ class RenameLineageTest(BaseCertManagerTest):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RenameLineageTest, self).setUp()
|
super(RenameLineageTest, self).setUp()
|
||||||
self.mock_config = configuration.RenewerConfiguration(
|
self.mock_config = configuration.NamespaceConfig(
|
||||||
namespace=mock.MagicMock(
|
namespace=mock.MagicMock(
|
||||||
config_dir=self.tempdir,
|
config_dir=self.tempdir,
|
||||||
work_dir=self.tempdir,
|
work_dir=self.tempdir,
|
||||||
@@ -307,7 +317,7 @@ class RenameLineageTest(BaseCertManagerTest):
|
|||||||
from certbot import cert_manager
|
from certbot import cert_manager
|
||||||
return cert_manager.rename_lineage(*args, **kwargs)
|
return cert_manager.rename_lineage(*args, **kwargs)
|
||||||
|
|
||||||
@mock.patch('certbot.renewal.renewal_conf_files')
|
@mock.patch('certbot.storage.renewal_conf_files')
|
||||||
@mock.patch('certbot.main.zope.component.getUtility')
|
@mock.patch('certbot.main.zope.component.getUtility')
|
||||||
def test_no_certname(self, mock_get_utility, mock_renewal_conf_files):
|
def test_no_certname(self, mock_get_utility, mock_renewal_conf_files):
|
||||||
mock_config = mock.Mock(certname=None, new_certname="two")
|
mock_config = mock.Mock(certname=None, new_certname="two")
|
||||||
|
|||||||
@@ -88,31 +88,19 @@ class NamespaceConfigTest(unittest.TestCase):
|
|||||||
self.assertTrue(os.path.isabs(config.key_dir))
|
self.assertTrue(os.path.isabs(config.key_dir))
|
||||||
self.assertTrue(os.path.isabs(config.temp_checkpoint_dir))
|
self.assertTrue(os.path.isabs(config.temp_checkpoint_dir))
|
||||||
|
|
||||||
|
|
||||||
class RenewerConfigurationTest(unittest.TestCase):
|
|
||||||
"""Test for certbot.configuration.RenewerConfiguration."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.namespace = mock.MagicMock(config_dir='/tmp/config')
|
|
||||||
from certbot.configuration import RenewerConfiguration
|
|
||||||
self.config = RenewerConfiguration(self.namespace)
|
|
||||||
|
|
||||||
@mock.patch('certbot.configuration.constants')
|
@mock.patch('certbot.configuration.constants')
|
||||||
def test_dynamic_dirs(self, constants):
|
def test_renewal_dynamic_dirs(self, constants):
|
||||||
constants.ARCHIVE_DIR = 'a'
|
constants.ARCHIVE_DIR = 'a'
|
||||||
constants.LIVE_DIR = 'l'
|
constants.LIVE_DIR = 'l'
|
||||||
constants.RENEWAL_CONFIGS_DIR = 'renewal_configs'
|
constants.RENEWAL_CONFIGS_DIR = 'renewal_configs'
|
||||||
constants.RENEWER_CONFIG_FILENAME = 'r.conf'
|
|
||||||
|
|
||||||
self.assertEqual(self.config.default_archive_dir, '/tmp/config/a')
|
self.assertEqual(self.config.default_archive_dir, '/tmp/config/a')
|
||||||
self.assertEqual(self.config.live_dir, '/tmp/config/l')
|
self.assertEqual(self.config.live_dir, '/tmp/config/l')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.config.renewal_configs_dir, '/tmp/config/renewal_configs')
|
self.config.renewal_configs_dir, '/tmp/config/renewal_configs')
|
||||||
self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf')
|
|
||||||
|
|
||||||
def test_absolute_paths(self):
|
def test_renewal_absolute_paths(self):
|
||||||
from certbot.configuration import NamespaceConfig
|
from certbot.configuration import NamespaceConfig
|
||||||
from certbot.configuration import RenewerConfiguration
|
|
||||||
|
|
||||||
config_base = "foo"
|
config_base = "foo"
|
||||||
work_base = "bar"
|
work_base = "bar"
|
||||||
@@ -125,12 +113,11 @@ class RenewerConfigurationTest(unittest.TestCase):
|
|||||||
mock_namespace.config_dir = config_base
|
mock_namespace.config_dir = config_base
|
||||||
mock_namespace.work_dir = work_base
|
mock_namespace.work_dir = work_base
|
||||||
mock_namespace.logs_dir = logs_base
|
mock_namespace.logs_dir = logs_base
|
||||||
config = RenewerConfiguration(NamespaceConfig(mock_namespace))
|
config = NamespaceConfig(mock_namespace)
|
||||||
|
|
||||||
self.assertTrue(os.path.isabs(config.default_archive_dir))
|
self.assertTrue(os.path.isabs(config.default_archive_dir))
|
||||||
self.assertTrue(os.path.isabs(config.live_dir))
|
self.assertTrue(os.path.isabs(config.live_dir))
|
||||||
self.assertTrue(os.path.isabs(config.renewal_configs_dir))
|
self.assertTrue(os.path.isabs(config.renewal_configs_dir))
|
||||||
self.assertTrue(os.path.isabs(config.renewer_config_file))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -567,6 +567,11 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||||||
self._call_no_clientmock(['certificates'])
|
self._call_no_clientmock(['certificates'])
|
||||||
self.assertEqual(1, mock_cert_manager.call_count)
|
self.assertEqual(1, mock_cert_manager.call_count)
|
||||||
|
|
||||||
|
@mock.patch('certbot.cert_manager.delete')
|
||||||
|
def test_delete(self, mock_cert_manager):
|
||||||
|
self._call_no_clientmock(['delete'])
|
||||||
|
self.assertEqual(1, mock_cert_manager.call_count)
|
||||||
|
|
||||||
def test_plugins(self):
|
def test_plugins(self):
|
||||||
flags = ['--init', '--prepare', '--authenticators', '--installers']
|
flags = ['--init', '--prepare', '--authenticators', '--installers']
|
||||||
for args in itertools.chain(
|
for args in itertools.chain(
|
||||||
@@ -856,8 +861,7 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||||||
rc_path = test_util.make_lineage(self, 'sample-renewal-ancient.conf')
|
rc_path = test_util.make_lineage(self, 'sample-renewal-ancient.conf')
|
||||||
args = mock.MagicMock(account=None, email=None, webroot_path=None)
|
args = mock.MagicMock(account=None, email=None, webroot_path=None)
|
||||||
config = configuration.NamespaceConfig(args)
|
config = configuration.NamespaceConfig(args)
|
||||||
lineage = storage.RenewableCert(rc_path,
|
lineage = storage.RenewableCert(rc_path, config)
|
||||||
configuration.RenewerConfiguration(config))
|
|
||||||
renewalparams = lineage.configuration["renewalparams"]
|
renewalparams = lineage.configuration["renewalparams"]
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
renewal._restore_webroot_config(config, renewalparams)
|
renewal._restore_webroot_config(config, renewalparams)
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ class RenewalTest(unittest.TestCase):
|
|||||||
rc_path = util.make_lineage(self, 'sample-renewal-ancient.conf')
|
rc_path = util.make_lineage(self, 'sample-renewal-ancient.conf')
|
||||||
args = mock.MagicMock(account=None, email=None, webroot_path=None)
|
args = mock.MagicMock(account=None, email=None, webroot_path=None)
|
||||||
config = configuration.NamespaceConfig(args)
|
config = configuration.NamespaceConfig(args)
|
||||||
lineage = storage.RenewableCert(
|
lineage = storage.RenewableCert(rc_path, config)
|
||||||
rc_path, configuration.RenewerConfiguration(config))
|
|
||||||
renewalparams = lineage.configuration["renewalparams"]
|
renewalparams = lineage.configuration["renewalparams"]
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
from certbot import renewal
|
from certbot import renewal
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class BaseRenewableCertTest(unittest.TestCase):
|
|||||||
from certbot import storage
|
from certbot import storage
|
||||||
self.tempdir = tempfile.mkdtemp()
|
self.tempdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
self.cli_config = configuration.RenewerConfiguration(
|
self.cli_config = configuration.NamespaceConfig(
|
||||||
namespace=mock.MagicMock(
|
namespace=mock.MagicMock(
|
||||||
config_dir=self.tempdir,
|
config_dir=self.tempdir,
|
||||||
work_dir=self.tempdir,
|
work_dir=self.tempdir,
|
||||||
@@ -57,16 +57,22 @@ class BaseRenewableCertTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: maybe provide RenewerConfiguration.make_dirs?
|
# TODO: maybe provide NamespaceConfig.make_dirs?
|
||||||
# TODO: main() should create those dirs, c.f. #902
|
# TODO: main() should create those dirs, c.f. #902
|
||||||
os.makedirs(os.path.join(self.tempdir, "live", "example.org"))
|
os.makedirs(os.path.join(self.tempdir, "live", "example.org"))
|
||||||
os.makedirs(os.path.join(self.tempdir, "archive", "example.org"))
|
archive_path = os.path.join(self.tempdir, "archive", "example.org")
|
||||||
|
os.makedirs(archive_path)
|
||||||
os.makedirs(os.path.join(self.tempdir, "renewal"))
|
os.makedirs(os.path.join(self.tempdir, "renewal"))
|
||||||
|
|
||||||
config = configobj.ConfigObj()
|
config = configobj.ConfigObj()
|
||||||
for kind in ALL_FOUR:
|
for kind in ALL_FOUR:
|
||||||
config[kind] = os.path.join(self.tempdir, "live", "example.org",
|
kind_path = os.path.join(self.tempdir, "live", "example.org",
|
||||||
kind + ".pem")
|
kind + ".pem")
|
||||||
|
config[kind] = kind_path
|
||||||
|
with open(os.path.join(self.tempdir, "live", "example.org",
|
||||||
|
"README"), 'a'):
|
||||||
|
pass
|
||||||
|
config["archive"] = archive_path
|
||||||
config.filename = os.path.join(self.tempdir, "renewal",
|
config.filename = os.path.join(self.tempdir, "renewal",
|
||||||
"example.org.conf")
|
"example.org.conf")
|
||||||
config.write()
|
config.write()
|
||||||
@@ -770,5 +776,95 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||||||
storage.RenewableCert(self.config.filename, self.cli_config,
|
storage.RenewableCert(self.config.filename, self.cli_config,
|
||||||
update_symlinks=True)
|
update_symlinks=True)
|
||||||
|
|
||||||
|
class DeleteFilesTest(BaseRenewableCertTest):
|
||||||
|
"""Tests for certbot.storage.delete_files"""
|
||||||
|
def setUp(self):
|
||||||
|
super(DeleteFilesTest, self).setUp()
|
||||||
|
for kind in ALL_FOUR:
|
||||||
|
kind_path = os.path.join(self.tempdir, "live", "example.org",
|
||||||
|
kind + ".pem")
|
||||||
|
with open(kind_path, 'a'):
|
||||||
|
pass
|
||||||
|
self.config.write()
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.renewal_configs_dir, "example.org.conf")))
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.tempdir, "archive", "example.org")))
|
||||||
|
|
||||||
|
def _call(self):
|
||||||
|
from certbot import storage
|
||||||
|
with mock.patch("certbot.storage.logger"):
|
||||||
|
storage.delete_files(self.cli_config, "example.org")
|
||||||
|
|
||||||
|
def test_delete_all_files(self):
|
||||||
|
self._call()
|
||||||
|
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.renewal_configs_dir, "example.org.conf")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.tempdir, "archive", "example.org")))
|
||||||
|
|
||||||
|
def test_bad_renewal_config(self):
|
||||||
|
with open(self.config.filename, 'a') as config_file:
|
||||||
|
config_file.write("asdfasfasdfasdf")
|
||||||
|
|
||||||
|
self.assertRaises(errors.CertStorageError, self._call)
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.renewal_configs_dir, "example.org.conf")))
|
||||||
|
|
||||||
|
def test_no_renewal_config(self):
|
||||||
|
os.remove(self.config.filename)
|
||||||
|
self.assertRaises(errors.CertStorageError, self._call)
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(self.config.filename))
|
||||||
|
|
||||||
|
def test_no_cert_file(self):
|
||||||
|
os.remove(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org", "cert.pem"))
|
||||||
|
self._call()
|
||||||
|
self.assertFalse(os.path.exists(self.config.filename))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.tempdir, "archive", "example.org")))
|
||||||
|
|
||||||
|
def test_no_readme_file(self):
|
||||||
|
os.remove(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org", "README"))
|
||||||
|
self._call()
|
||||||
|
self.assertFalse(os.path.exists(self.config.filename))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.tempdir, "archive", "example.org")))
|
||||||
|
|
||||||
|
def test_livedir_not_empty(self):
|
||||||
|
with open(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org", "other_file"), 'a'):
|
||||||
|
pass
|
||||||
|
self._call()
|
||||||
|
self.assertFalse(os.path.exists(self.config.filename))
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.tempdir, "archive", "example.org")))
|
||||||
|
|
||||||
|
def test_no_archive(self):
|
||||||
|
archive_dir = os.path.join(self.tempdir, "archive", "example.org")
|
||||||
|
os.rmdir(archive_dir)
|
||||||
|
self._call()
|
||||||
|
self.assertFalse(os.path.exists(self.config.filename))
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(
|
||||||
|
self.cli_config.live_dir, "example.org")))
|
||||||
|
self.assertFalse(os.path.exists(archive_dir))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main() # pragma: no cover
|
unittest.main() # pragma: no cover
|
||||||
|
|||||||
Reference in New Issue
Block a user