Merge pull request #650 from letsencrypt/renewer_errors

Renewer errors
This commit is contained in:
James Kasten 2015-08-04 15:50:09 -07:00
commit 264a7274ad
4 changed files with 57 additions and 34 deletions

View file

@ -5,10 +5,6 @@ class Error(Exception):
"""Generic Let's Encrypt client error."""
class SubprocessError(Error):
"""Subprocess handling error."""
class AccountStorageError(Error):
"""Generic `.AccountStorage` error."""
@ -21,6 +17,14 @@ class ReverterError(Error):
"""Let's Encrypt Reverter error."""
class SubprocessError(Error):
"""Subprocess handling error."""
class CertStorageError(Error):
"""Generic `.CertStorage` error."""
# Auth Handler Errors
class AuthorizationError(Error):
"""Authorization error."""

View file

@ -20,6 +20,7 @@ from letsencrypt import configuration
from letsencrypt import cli
from letsencrypt import client
from letsencrypt import crypto_util
from letsencrypt import errors
from letsencrypt import notify
from letsencrypt import storage
@ -164,7 +165,7 @@ def main(config=None, args=sys.argv[1:]):
# dramatically improve performance for large deployments
# where autorenewal is widely turned off.
cert = storage.RenewableCert(rc_config, cli_config=cli_config)
except ValueError:
except errors.CertStorageError:
# This indicates an invalid renewal configuration file, such
# as one missing a required parameter (in the future, perhaps
# also one that is internally inconsistent or is missing a

View file

@ -11,6 +11,7 @@ import pytz
import pyrfc3339
from letsencrypt import constants
from letsencrypt import errors
from letsencrypt import le_util
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
@ -90,7 +91,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
renewal config file.
:param .RenewerConfiguration cli_config:
:raises ValueError: 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.
:raises TypeError: if the provided renewal configuration isn't a
ConfigObj object.
@ -99,7 +100,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
self.cli_config = cli_config
if isinstance(configfile, configobj.ConfigObj):
if not os.path.basename(configfile.filename).endswith(".conf"):
raise ValueError("renewal config file name must end in .conf")
raise errors.CertStorageError(
"renewal config file name must end in .conf")
self.lineagename = os.path.basename(
configfile.filename)[:-len(".conf")]
else:
@ -117,8 +119,9 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
self.configuration.merge(self.configfile)
if not all(x in self.configuration for x in ALL_FOUR):
raise ValueError("renewal config file {0} is missing a required "
"file reference".format(configfile))
raise errors.CertStorageError(
"renewal config file {0} is missing a required "
"file reference".format(configfile))
self.cert = self.configuration["cert"]
self.privkey = self.configuration["privkey"]
@ -213,7 +216,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""
if kind not in ALL_FOUR:
raise ValueError("unknown kind of item")
raise errors.CertStorageError("unknown kind of item")
link = getattr(self, kind)
if not os.path.exists(link):
return None
@ -236,7 +239,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""
if kind not in ALL_FOUR:
raise ValueError("unknown kind of item")
raise errors.CertStorageError("unknown kind of item")
pattern = re.compile(r"^{0}([0-9]+)\.pem$".format(kind))
target = self.current_target(kind)
if target is None or not os.path.exists(target):
@ -263,12 +266,12 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""
if kind not in ALL_FOUR:
raise ValueError("unknown kind of item")
raise errors.CertStorageError("unknown kind of item")
where = os.path.dirname(self.current_target(kind))
return os.path.join(where, "{0}{1}.pem".format(kind, version))
def available_versions(self, kind):
"""Which lternative versions of the specified kind of item exist?
"""Which alternative versions of the specified kind of item exist?
The archive directory where the current version is stored is
consulted to obtain the list of alternatives.
@ -281,7 +284,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""
if kind not in ALL_FOUR:
raise ValueError("unknown kind of item")
raise errors.CertStorageError("unknown kind of item")
where = os.path.dirname(self.current_target(kind))
files = os.listdir(where)
pattern = re.compile(r"^{0}([0-9]+)\.pem$".format(kind))
@ -308,7 +311,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
:rtype: int
"""
# TODO: this can raise ValueError if there is no version overlap
# TODO: this can raise CertStorageError if there is no version overlap
# (it should probably return None instead)
# TODO: this can raise a spurious AttributeError if the current
# link for any kind is missing (it should probably return None)
@ -355,7 +358,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""
if kind not in ALL_FOUR:
raise ValueError("unknown kind of item")
raise errors.CertStorageError("unknown kind of item")
link = getattr(self, kind)
filename = "{0}{1}.pem".format(kind, version)
# Relative rather than absolute target directory
@ -550,7 +553,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
config_file, config_filename = le_util.unique_lineage_name(
cli_config.renewal_configs_dir, lineagename)
if not config_filename.endswith(".conf"):
raise ValueError("renewal config file name must end in .conf")
raise errors.CertStorageError(
"renewal config file name must end in .conf")
# Determine where on disk everything will go
# lineagename will now potentially be modified based on which
@ -559,9 +563,11 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
archive = os.path.join(cli_config.archive_dir, lineagename)
live_dir = os.path.join(cli_config.live_dir, lineagename)
if os.path.exists(archive):
raise ValueError("archive directory exists for " + lineagename)
raise errors.CertStorageError(
"archive directory exists for " + lineagename)
if os.path.exists(live_dir):
raise ValueError("live directory exists for " + lineagename)
raise errors.CertStorageError(
"live directory exists for " + lineagename)
os.mkdir(archive)
os.mkdir(live_dir)
relative_archive = os.path.join("..", "..", "archive", lineagename)

View file

@ -10,6 +10,7 @@ import mock
import pytz
from letsencrypt import configuration
from letsencrypt import errors
from letsencrypt.storage import ALL_FOUR
from letsencrypt.tests import test_util
@ -78,7 +79,8 @@ class RenewableCertTests(unittest.TestCase):
for kind in ALL_FOUR:
config["cert"] = "nonexistent_" + kind + ".pem"
config.filename = "nonexistent_sillyfile"
self.assertRaises(ValueError, storage.RenewableCert, config, defaults)
self.assertRaises(
errors.CertStorageError, storage.RenewableCert, config, defaults)
self.assertRaises(TypeError, storage.RenewableCert, "fun", defaults)
def test_renewal_incomplete_config(self):
@ -92,7 +94,8 @@ class RenewableCertTests(unittest.TestCase):
config["chain"] = "imaginary_chain.pem"
config["fullchain"] = "imaginary_fullchain.pem"
config.filename = "imaginary_config.conf"
self.assertRaises(ValueError, storage.RenewableCert, config, defaults)
self.assertRaises(
errors.CertStorageError, storage.RenewableCert, config, defaults)
def test_consistent(self): # pylint: disable=too-many-statements
oldcert = self.test_rc.cert
@ -481,11 +484,13 @@ class RenewableCertTests(unittest.TestCase):
# Now trigger the detection of already existing files
os.mkdir(os.path.join(
self.cli_config.live_dir, "the-lineage.com-0002"))
self.assertRaises(ValueError, storage.RenewableCert.new_lineage,
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage,
"the-lineage.com", "cert3", "privkey3", "chain3",
None, self.defaults, self.cli_config)
os.mkdir(os.path.join(self.cli_config.archive_dir, "other-example.com"))
self.assertRaises(ValueError, storage.RenewableCert.new_lineage,
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage,
"other-example.com", "cert4", "privkey4", "chain4",
None, self.defaults, self.cli_config)
# Make sure it can accept renewal parameters
@ -518,20 +523,27 @@ class RenewableCertTests(unittest.TestCase):
def test_invalid_config_filename(self, mock_uln):
from letsencrypt import storage
mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes"
self.assertRaises(ValueError, storage.RenewableCert.new_lineage,
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage,
"example.com", "cert", "privkey", "chain",
None, self.defaults, self.cli_config)
def test_bad_kind(self):
self.assertRaises(ValueError, self.test_rc.current_target, "elephant")
self.assertRaises(ValueError, self.test_rc.current_version, "elephant")
self.assertRaises(ValueError, self.test_rc.version, "elephant", 17)
self.assertRaises(ValueError, self.test_rc.available_versions,
"elephant")
self.assertRaises(ValueError, self.test_rc.newest_available_version,
"elephant")
self.assertRaises(ValueError, self.test_rc.update_link_to,
"elephant", 17)
self.assertRaises(
errors.CertStorageError, self.test_rc.current_target, "elephant")
self.assertRaises(
errors.CertStorageError, self.test_rc.current_version, "elephant")
self.assertRaises(
errors.CertStorageError, self.test_rc.version, "elephant", 17)
self.assertRaises(
errors.CertStorageError,
self.test_rc.available_versions, "elephant")
self.assertRaises(
errors.CertStorageError,
self.test_rc.newest_available_version, "elephant")
self.assertRaises(
errors.CertStorageError,
self.test_rc.update_link_to, "elephant", 17)
def test_ocsp_revoked(self):
# XXX: This is currently hardcoded to False due to a lack of an
@ -651,7 +663,7 @@ class RenewableCertTests(unittest.TestCase):
f.write("incomplete = configfile\n")
renewer.main(self.defaults, args=[
'--config-dir', self.cli_config.config_dir])
# The ValueError is caught inside and nothing happens.
# The errors.CertStorageError is caught inside and nothing happens.
if __name__ == "__main__":