iteration

This commit is contained in:
James Kasten 2015-02-24 11:53:57 -08:00
parent 17741a0fbf
commit ad1dfc4701
10 changed files with 88 additions and 68 deletions

View file

@ -214,7 +214,7 @@ class Client(object):
:param redirect: If traffic should be forwarded from HTTP to HTTPS.
:type redirect: bool or None
:raises :class:`letsencrypt.client.errors.LetsEncryptClientError`: if
:raises letsencrypt.client.errors.LetsEncryptClientError: if
no installer is specified in the client.
"""
@ -261,7 +261,8 @@ def validate_key_csr(privkey, csr=None):
:param csr: CSR
:type csr: :class:`letsencrypt.client.le_util.CSR`
:raises LetsEncryptClientError: if validation fails
:raises letsencrypt.client.errors.LetsEncryptClientError: when
validation fails
"""
# TODO: Handle all of these problems appropriately
@ -355,7 +356,7 @@ def determine_authenticator(all_auths):
:returns: Valid Authenticator object or None
:raises :class:`letsencrypt.client.errors.LetsEncryptClientError`: If no
:raises letsencrypt.client.errors.LetsEncryptClientError: If no
authenticator is available.
"""
@ -380,11 +381,11 @@ def determine_authenticator(all_auths):
else:
raise errors.LetsEncryptClientError("No Authenticators available.")
if auth in errs:
if auth and auth in errs:
logging.error("Please fix the configuration for the Authenticator. "
"The following error message was received: "
"%s", errs[auth])
sys.exit(1)
return
return auth

View file

@ -21,7 +21,7 @@ def ask(enhancement):
:returns: True if feature is desired, False otherwise
:rtype: bool
:raises :class:`letsencrypt.client.errors.LetsEncryptClientError`: If
:raises letsencrypt.client.errors.LetsEncryptClientError: If
the enhancement provided is not supported.
"""

View file

@ -1,6 +1,5 @@
"""Contains UI methods for LE user operations."""
import os
import sys
import zope.component
@ -19,7 +18,7 @@ def choose_authenticator(auths, errs):
:param dict errs: Mapping IAuthenticator objects to error messages
:returns: Authenticator selected
:rtype: :class:`letsencrypt.client.interfaces.IAuthenticator`
:rtype: :class:`letsencrypt.client.interfaces.IAuthenticator` or `None`
"""
descs = [auth.description if auth not in errs
@ -41,8 +40,7 @@ def choose_authenticator(auths, errs):
util(interfaces.IDisplay).notification(
msg, height=display_util.HEIGHT)
else:
sys.exit(0)
return
def choose_names(installer):
"""Display screen to select domains to validate.
@ -50,6 +48,9 @@ def choose_names(installer):
:param installer: An installer object
:type installer: :class:`letsencrypt.client.interfaces.IInstaller`
:returns: List of selected names
:rtype: `list` of `str`
"""
if installer is None:
return _choose_names_manually()
@ -67,13 +68,13 @@ def choose_names(installer):
if manual:
return _choose_names_manually()
else:
sys.exit(0)
return []
code, names = _filter_names(names)
if code == display_util.OK and names:
return names
else:
sys.exit(0)
return []
def _filter_names(names):
@ -101,8 +102,8 @@ def _choose_names_manually():
if code == display_util.OK:
return display_util.separate_list_input(input_)
sys.exit(0)
# Else just return None
return []
def success_installation(domains):

View file

@ -18,10 +18,10 @@ class IAuthenticator(zope.interface.Interface):
Finish up any additional initialization.
:raises :class:`~.errors.LetsEncryptMisconfigurationError`: when
full initialization cannot be completed.
:raises :class:`~.errors.LetsEncryptNoInstallationError`: when the
necessary programs/files cannot be located.
:raises letsencrypt.client.errors.LetsEncryptMisconfigurationError:
when full initialization cannot be completed.
:raises letsencrypt.client.errors.LetsEncryptNoInstallationError:
when the necessary programs/files cannot be located.
"""
@ -135,10 +135,10 @@ class IInstaller(zope.interface.Interface):
Finish up any additional initialization.
:raises :class:`~.errors.LetsEncryptMisconfigurationError`: when
full initialization cannot be completed.
:raises :class:`~.errors.LetsEncryptNoInstallationError`: when the
necessary programs/files cannot be located.
:raises letsencrypt.client.errors.LetsEncryptMisconfigurationError`:
when full initialization cannot be completed.
:raises letsencrypt.errors.LetsEncryptNoInstallationError`:
when the necessary programs/files cannot be located.
"""

View file

@ -28,8 +28,8 @@ class Reverter(object):
This function should reinstall the users original configuration files
for all saves with temporary=True
:raises :class:`errors.LetsEncryptReverterError`:
Unable to revert config
:raises letsencrypt.client.errors.LetsEncryptReverterError: when
unable to revert config
"""
if os.path.isdir(self.config.temp_checkpoint_dir):
@ -48,7 +48,7 @@ class Reverter(object):
:param int rollback: Number of checkpoints to reverse. A str num will be
cast to an integer. So "2" is also acceptable.
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError`: If
:raises letsencrypt.client.errors.LetsEncryptReverterError: If
there is a problem with the input or if the function is unable to
correctly revert the configuration checkpoints.
@ -159,7 +159,7 @@ class Reverter(object):
:param str save_notes: notes about changes made during the save
:raises IOError: If unable to open cp_dir + FILEPATHS file
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError: If
:raises letsencrypt.client.errors.LetsEncryptReverterError: If
unable to add checkpoint
"""
@ -252,7 +252,7 @@ class Reverter(object):
:param set save_files: Set of files about to be saved.
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError`:
:raises letsencrypt.client.errors.LetsEncryptReverterError:
when save is attempting to overwrite a temporary file.
"""
@ -288,7 +288,7 @@ class Reverter(object):
a temp or permanent save.
:param \*files: file paths (str) to be registered
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError`: If
:raises letsencrypt.client.errors.LetsEncryptReverterError: If
call does not contain necessary parameters or if the file creation
is unable to be registered.
@ -354,7 +354,7 @@ class Reverter(object):
:returns: Success
:rtype: bool
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError`: If
:raises letsencrypt.client.errors.LetsEncryptReverterError: If
all files within file_list cannot be removed
"""
@ -393,7 +393,8 @@ class Reverter(object):
:param str title: Title describing checkpoint
:raises :class:`letsencrypt.client.errors.LetsEncryptReverterError`
:raises letsencrypt.client.errors.LetsEncryptReverterError: when the
checkpoint is not able to be finalized.
"""
# Check to make sure an "in progress" directory exists

View file

@ -82,10 +82,8 @@ class Revoker(object):
# certificate.
_, b_k = self._row_to_backup(row)
try:
if clean_pem == Crypto.PublicKey.RSA.importKey(
open(b_k).read()).exportKey("PEM"):
certs.append(
Cert.fromrow(row, self.config.cert_key_backup))
test_pem = Crypto.PublicKey.RSA.importKey(
open(b_k).read()).exportKey("PEM")
except (IndexError, ValueError, TypeError):
# This should never happen given the assumptions of the
# module. If it does, it is probably best to delete the
@ -93,6 +91,9 @@ class Revoker(object):
raise errors.LetsEncryptRevokerError(
"%s - backup file is corrupted.")
if clean_pem == test_pem:
certs.append(
Cert.fromrow(row, self.config.cert_key_backup))
if certs:
self._safe_revoke(certs)
else:
@ -216,13 +217,12 @@ class Revoker(object):
if self.no_confirm or revocation.confirm_revocation(cert):
try:
self._acme_revoke(cert)
success_list.append(cert)
revocation.success_revocation(cert)
except errors.LetsEncryptClientError:
# TODO: Improve error handling when networking is set...
logging.error(
"Unable to revoke cert:%s%s", os.linesep, str(cert))
success_list.append(cert)
revocation.success_revocation(cert)
finally:
if success_list:
self._remove_certs_keys(success_list)

View file

@ -59,7 +59,7 @@ class DetermineAuthenticatorTest(unittest.TestCase):
errors.LetsEncryptMisconfigurationError)
mock_choose.return_value = self.mock_apache
self.assertRaises(SystemExit, self._call, self.all_auths)
self.assertRaises(self._call(self.all_auths), None)
class RollbackTest(unittest.TestCase):

View file

@ -51,7 +51,7 @@ class ChooseAuthenticatorTest(unittest.TestCase):
def test_no_choice(self, mock_util):
mock_util().menu.return_value = (display_util.CANCEL, 0)
self.assertRaises(SystemExit, self._call, self.auths, {})
self.assertEqual(self._call(self.auths, {}), None)
class GenHttpsNamesTest(unittest.TestCase):
@ -68,14 +68,22 @@ class GenHttpsNamesTest(unittest.TestCase):
self.assertEqual(self._call([]), "")
def test_one(self):
dom = "example.com"
self.assertEqual(self._call([dom]), "https://%s" % dom)
doms = [
"example.com",
"asllkjsadfljasdf.c",
]
for dom in doms:
self.assertEqual(self._call([dom]), "https://%s" % dom)
def test_two(self):
doms = ["foo.bar.org", "bar.org"]
self.assertEqual(
self._call(doms),
"https://{dom[0]} and https://{dom[1]}".format(dom=doms))
domains_list = [
["foo.bar.org", "bar.org"],
["paypal.google.facebook.live.com", "*.zombo.example.com"],
]
for doms in domains_list:
self.assertEqual(
self._call(doms),
"https://{dom[0]} and https://{dom[1]}".format(dom=doms))
def test_three(self):
doms = ["a.org", "b.org", "c.org"]
@ -112,7 +120,7 @@ class ChooseNamesTest(unittest.TestCase):
@mock.patch("letsencrypt.client.display.ops.util")
def test_no_installer_cancel(self, mock_util):
mock_util().input.return_value = (display_util.CANCEL, [])
self.assertRaises(SystemExit, self._call, None)
self.assertEqual(self._call(None), [])
@mock.patch("letsencrypt.client.display.ops.util")
def test_no_names_choose(self, mock_util):
@ -130,7 +138,7 @@ class ChooseNamesTest(unittest.TestCase):
self.mock_install().get_all_names.return_value = set()
mock_util().yesno.return_value = False
self.assertRaises(SystemExit, self._call, self.mock_install)
self.assertEqual(self._call(self.mock_install), [])
@mock.patch("letsencrypt.client.display.ops.util")
def test_filter_names_valid_return(self, mock_util):
@ -146,7 +154,7 @@ class ChooseNamesTest(unittest.TestCase):
self.mock_install.get_all_names.return_value = set(["example.com"])
mock_util().checklist.return_value = (display_util.OK, [])
self.assertRaises(SystemExit, self._call, self.mock_install)
self.assertEqual(self._call(self.mock_install), [])
@mock.patch("letsencrypt.client.display.ops.util")
def test_filter_names_cancel(self, mock_util):
@ -154,7 +162,7 @@ class ChooseNamesTest(unittest.TestCase):
mock_util().checklist.return_value = (
display_util.CANCEL, ["example.com"])
self.assertRaises(SystemExit, self._call, self.mock_install)
self.assertEqual(self._call(self.mock_install), [])
class SuccessInstallationTest(unittest.TestCase):

View file

@ -265,10 +265,10 @@ class FileOutputDisplayTest(DisplayT):
def test_wrap_lines(self):
# pylint: disable=protected-access
msg = ("This is just a weak test\n"
"This function is only meant to be for easy viewing\n"
msg = ("This is just a weak test{0}"
"This function is only meant to be for easy viewing{0}"
"Test a really really really really really really really really "
"really really really really really long line...")
"really really really really long line...".format(os.linesep))
text = self.displayer._wrap_lines(msg)
self.assertEqual(text.count(os.linesep), 3)
@ -313,20 +313,20 @@ class SeparateListInputTest(unittest.TestCase):
return separate_list_input(input_)
def test_commas(self):
actual = self._call("a,b,c,test")
self.assertEqual(actual, self.exp)
self.assertEqual(self._call("a,b,c,test"), self.exp)
def test_spaces(self):
actual = self._call("a b c test")
self.assertEqual(actual, self.exp)
self.assertEqual(self._call("a b c test"), self.exp)
def test_both(self):
actual = self._call("a, b, c, test")
self.assertEqual(actual, self.exp)
self.assertEqual(self._call("a, b, c, test"), self.exp)
def test_mess(self):
actual = [self._call(" a , b c \t test")]
actual.append(self._call(",a, ,, , b c test "))
actual = [
self._call(" a , b c \t test"),
self._call(",a, ,, , b c test "),
self._call(",,,,, , a b,,, , c,test"),
]
for act in actual:
self.assertEqual(act, self.exp)
@ -339,12 +339,11 @@ class PlaceParensTest(unittest.TestCase):
return _parens_around_char(label)
def test_single_letter(self):
ret = self._call("a")
self.assertEqual("(a)", ret)
self.assertEqual("(a)", self._call("a"))
def test_multiple(self):
ret = self._call("Label")
self.assertEqual("(L)abel", ret)
self.assertEqual("(L)abel", self._call("Label"))
self.assertEqual("(y)es please", self._call("yes please"))
if __name__ == "__main__":

View file

@ -16,6 +16,7 @@ import letsencrypt
from letsencrypt.client import configuration
from letsencrypt.client import client
from letsencrypt.client import errors
from letsencrypt.client import interfaces
from letsencrypt.client import le_util
from letsencrypt.client import log
@ -97,7 +98,7 @@ def create_parser():
return parser
def main(): # pylint: disable=too-many-branches
def main(): # pylint: disable=too-many-branches, too-many-statements
"""Command line argument parsing and main script execution."""
# note: arg parser internally handles --help (and exits afterwards)
args = create_parser().parse_args()
@ -139,10 +140,16 @@ def main(): # pylint: disable=too-many-branches
configurator.ApacheConfigurator(config),
standalone.StandaloneAuthenticator(),
]
auth = client.determine_authenticator(all_auths)
try:
auth = client.determine_authenticator(all_auths)
except errors.LetsEncryptClientError:
logging.critical("No authentication mechanisms were found on your "
"system.")
sys.exit(1)
if auth is None:
logging.critical("Unable to find a way to authenticate the server.")
sys.exit(4)
logging.info("Cannot authenticate to the ACME server.")
sys.exit(0)
# Use the same object if possible
if interfaces.IInstaller.providedBy(auth): # pylint: disable=no-member
@ -156,6 +163,9 @@ def main(): # pylint: disable=too-many-branches
else:
doms = args.domains
if not doms:
sys.exit(0)
# Prepare for init of Client
if args.authkey is None:
authkey = client.init_key(args.rsa_key_size, config.key_dir)