mirror of
https://github.com/certbot/certbot.git
synced 2026-06-09 08:42:57 -04:00
Merge remote-tracking branch 'letsencrypt/master'
This commit is contained in:
commit
5996318e40
17 changed files with 184 additions and 138 deletions
|
|
@ -58,7 +58,7 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \
|
|||
-e /opt/letsencrypt/src/letsencrypt-nginx \
|
||||
-e /opt/letsencrypt/src/letshelp-letsencrypt \
|
||||
-e /opt/letsencrypt/src/letsencrypt-compatibility-test \
|
||||
-e /opt/letsencrypt/src[dev,docs,testing]
|
||||
-e /opt/letsencrypt/src[dev,docs]
|
||||
|
||||
# install in editable mode (-e) to save space: it's not possible to
|
||||
# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image);
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ client will guide you through the process of obtaining and installing certs
|
|||
interactively.
|
||||
|
||||
You can also tell it exactly what you want it to do from the command line.
|
||||
For instance, if you want to obtain a cert for ``thing.com``,
|
||||
``www.thing.com``, and ``otherthing.net``, using the Apache plugin to both
|
||||
For instance, if you want to obtain a cert for ``example.com``,
|
||||
``www.example.com``, and ``other.example.net``, using the Apache plugin to both
|
||||
obtain and install the certs, you could do this::
|
||||
|
||||
./letsencrypt-auto --apache -d thing.com -d www.thing.com -d otherthing.net
|
||||
./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net
|
||||
|
||||
(The first time you run the command, it will make an account, and ask for an
|
||||
email and agreement to the Let's Encrypt Subscriber Agreement; you can
|
||||
|
|
@ -64,7 +64,7 @@ automate those with ``--email`` and ``--agree-tos``)
|
|||
If you want to use a webserver that doesn't have full plugin support yet, you
|
||||
can still use "standalone" or "webroot" plugins to obtain a certificate::
|
||||
|
||||
./letsencrypt-auto certonly --standalone --email admin@thing.com -d thing.com -d www.thing.com -d otherthing.net
|
||||
./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net
|
||||
|
||||
|
||||
Understanding the client in more depth
|
||||
|
|
|
|||
|
|
@ -180,40 +180,41 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|||
raise errors.UnexpectedUpdate(authzr)
|
||||
return authzr
|
||||
|
||||
def request_challenges(self, identifier, new_authzr_uri):
|
||||
def request_challenges(self, identifier, new_authzr_uri=None):
|
||||
"""Request challenges.
|
||||
|
||||
:param identifier: Identifier to be challenged.
|
||||
:type identifier: `.messages.Identifier`
|
||||
|
||||
:param str new_authzr_uri: new-authorization URI
|
||||
:param .messages.Identifier identifier: Identifier to be challenged.
|
||||
:param str new_authzr_uri: ``new-authorization`` URI. If omitted,
|
||||
will default to value found in ``directory``.
|
||||
|
||||
:returns: Authorization Resource.
|
||||
:rtype: `.AuthorizationResource`
|
||||
|
||||
"""
|
||||
new_authz = messages.NewAuthorization(identifier=identifier)
|
||||
response = self.net.post(new_authzr_uri, new_authz)
|
||||
response = self.net.post(self.directory.new_authz
|
||||
if new_authzr_uri is None else new_authzr_uri,
|
||||
new_authz)
|
||||
# TODO: handle errors
|
||||
assert response.status_code == http_client.CREATED
|
||||
return self._authzr_from_response(response, identifier)
|
||||
|
||||
def request_domain_challenges(self, domain, new_authz_uri):
|
||||
def request_domain_challenges(self, domain, new_authzr_uri=None):
|
||||
"""Request challenges for domain names.
|
||||
|
||||
This is simply a convenience function that wraps around
|
||||
`request_challenges`, but works with domain names instead of
|
||||
generic identifiers.
|
||||
generic identifiers. See ``request_challenges`` for more
|
||||
documentation.
|
||||
|
||||
:param str domain: Domain name to be challenged.
|
||||
:param str new_authzr_uri: new-authorization URI
|
||||
|
||||
:returns: Authorization Resource.
|
||||
:rtype: `.AuthorizationResource`
|
||||
|
||||
"""
|
||||
return self.request_challenges(messages.Identifier(
|
||||
typ=messages.IDENTIFIER_FQDN, value=domain), new_authz_uri)
|
||||
typ=messages.IDENTIFIER_FQDN, value=domain), new_authzr_uri)
|
||||
|
||||
def answer_challenge(self, challb, response):
|
||||
"""Answer challenge.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ class ClientTest(unittest.TestCase):
|
|||
'https://www.letsencrypt-demo.org/acme/new-reg',
|
||||
messages.Revocation:
|
||||
'https://www.letsencrypt-demo.org/acme/revoke-cert',
|
||||
messages.NewAuthorization:
|
||||
'https://www.letsencrypt-demo.org/acme/new-authz',
|
||||
})
|
||||
|
||||
from acme.client import Client
|
||||
|
|
@ -142,7 +144,7 @@ class ClientTest(unittest.TestCase):
|
|||
regr = self.client.update_registration.call_args[0][0]
|
||||
self.assertEqual(self.regr.terms_of_service, regr.body.agreement)
|
||||
|
||||
def test_request_challenges(self):
|
||||
def _prepare_response_for_request_challenges(self):
|
||||
self.response.status_code = http_client.CREATED
|
||||
self.response.headers['Location'] = self.authzr.uri
|
||||
self.response.json.return_value = self.authz.to_json()
|
||||
|
|
@ -150,10 +152,20 @@ class ClientTest(unittest.TestCase):
|
|||
'next': {'url': self.authzr.new_cert_uri},
|
||||
}
|
||||
|
||||
self.client.request_challenges(self.identifier, self.authzr.uri)
|
||||
# TODO: test POST call arguments
|
||||
def test_request_challenges(self):
|
||||
self._prepare_response_for_request_challenges()
|
||||
self.client.request_challenges(self.identifier)
|
||||
self.net.post.assert_called_once_with(
|
||||
self.directory.new_authz,
|
||||
messages.NewAuthorization(identifier=self.identifier))
|
||||
|
||||
# TODO: split here and separate test
|
||||
def test_requets_challenges_custom_uri(self):
|
||||
self._prepare_response_for_request_challenges()
|
||||
self.client.request_challenges(self.identifier, 'URI')
|
||||
self.net.post.assert_called_once_with('URI', mock.ANY)
|
||||
|
||||
def test_request_challenges_unexpected_update(self):
|
||||
self._prepare_response_for_request_challenges()
|
||||
self.response.json.return_value = self.authz.update(
|
||||
identifier=self.identifier.update(value='foo')).to_json()
|
||||
self.assertRaises(
|
||||
|
|
@ -162,15 +174,20 @@ class ClientTest(unittest.TestCase):
|
|||
|
||||
def test_request_challenges_missing_next(self):
|
||||
self.response.status_code = http_client.CREATED
|
||||
self.assertRaises(
|
||||
errors.ClientError, self.client.request_challenges,
|
||||
self.identifier, self.regr)
|
||||
self.assertRaises(errors.ClientError, self.client.request_challenges,
|
||||
self.identifier)
|
||||
|
||||
def test_request_domain_challenges(self):
|
||||
self.client.request_challenges = mock.MagicMock()
|
||||
self.assertEqual(
|
||||
self.client.request_challenges(self.identifier),
|
||||
self.client.request_domain_challenges('example.com', self.regr))
|
||||
self.client.request_domain_challenges('example.com'))
|
||||
|
||||
def test_request_domain_challenges_custom_uri(self):
|
||||
self.client.request_challenges = mock.MagicMock()
|
||||
self.assertEqual(
|
||||
self.client.request_challenges(self.identifier, 'URI'),
|
||||
self.client.request_domain_challenges('example.com', 'URI'))
|
||||
|
||||
def test_answer_challenge(self):
|
||||
self.response.links['up'] = {'url': self.challr.authzr_uri}
|
||||
|
|
|
|||
|
|
@ -34,17 +34,18 @@ if sys.version_info < (2, 7):
|
|||
else:
|
||||
install_requires.append('mock')
|
||||
|
||||
dev_extras = [
|
||||
'nose',
|
||||
'pep8',
|
||||
'tox',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
|
||||
'sphinx_rtd_theme',
|
||||
'sphinxcontrib-programoutput',
|
||||
]
|
||||
|
||||
testing_extras = [
|
||||
'nose',
|
||||
'tox',
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name='acme',
|
||||
|
|
@ -74,8 +75,8 @@ setup(
|
|||
include_package_data=True,
|
||||
install_requires=install_requires,
|
||||
extras_require={
|
||||
'dev': dev_extras,
|
||||
'docs': docs_extras,
|
||||
'testing': testing_extras,
|
||||
},
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
|
|
|||
|
|
@ -111,11 +111,11 @@ potentially be a separate directory for each domain. When requested a
|
|||
certificate for multiple domains, each domain will use the most recently
|
||||
specified ``--webroot-path``. So, for instance,
|
||||
|
||||
``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/eg -d eg.is -d www.eg.is``
|
||||
``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net``
|
||||
|
||||
would obtain a single certificate for all of those names, using the
|
||||
``/var/www/example`` webroot directory for the first two, and
|
||||
``/var/www/eg`` for the second two.
|
||||
``/var/www/other`` for the second two.
|
||||
|
||||
The webroot plugin works by creating a temporary file for each of your requested
|
||||
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
|
||||
|
|
@ -130,7 +130,7 @@ made to your web server would look like:
|
|||
Note that to use the webroot plugin, your server must be configured to serve
|
||||
files from hidden directories. If ``/.well-known`` is treated specially by
|
||||
your webserver configuration, you might need to modify the configuration
|
||||
to ensure that files inside ``/.well-known/ache-challenge`` are served by
|
||||
to ensure that files inside ``/.well-known/acme-challenge`` are served by
|
||||
the webserver.
|
||||
|
||||
Standalone
|
||||
|
|
@ -439,8 +439,8 @@ want to use the Apache plugin, it has to be installed separately:
|
|||
emerge -av app-crypt/letsencrypt
|
||||
emerge -av app-crypt/letsencrypt-apache
|
||||
|
||||
Currently, only the Apache plugin is included in Portage. However, if you
|
||||
want the nginx plugin, you can use Layman to add the mrueg overlay which
|
||||
Currently, only the Apache plugin is included in Portage. However, if you
|
||||
want the nginx plugin, you can use Layman to add the mrueg overlay which
|
||||
does include the nginx plugin package:
|
||||
|
||||
.. code-block:: shell
|
||||
|
|
@ -450,9 +450,9 @@ does include the nginx plugin package:
|
|||
layman -a mrueg
|
||||
emerge -av app-crypt/letsencrypt-nginx
|
||||
|
||||
When using the Apache plugin, you will run into a "cannot find a cert or key
|
||||
When using the Apache plugin, you will run into a "cannot find a cert or key
|
||||
directive" error if you're sporting the default Gentoo ``httpd.conf``.
|
||||
You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf``
|
||||
You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf``
|
||||
as follows:
|
||||
|
||||
Change
|
||||
|
|
@ -471,7 +471,7 @@ to
|
|||
LoadModule ssl_module modules/mod_ssl.so
|
||||
#</IfDefine>
|
||||
|
||||
For the time being, this is the only way for the Apache plugin to recognise
|
||||
For the time being, this is the only way for the Apache plugin to recognise
|
||||
the appropriate directives when installing the certificate.
|
||||
Note: this change is not required for the other plugins.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh".
|
|||
XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
||||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN=${VENV_PATH}/bin
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.5.0.dev0"
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
# This first clause is redundant with the third, but hedging on portability
|
||||
if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then
|
||||
VERBOSE=1
|
||||
elif [ "$arg" = "--no-self-upgrade" ] ; then
|
||||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1
|
||||
elif [ "$arg" = "--os-packages-only" ] ; then
|
||||
OS_PACKAGES_ONLY=1
|
||||
elif [ "$arg" = "--debug" ]; then
|
||||
DEBUG=1
|
||||
fi
|
||||
case "$arg" in
|
||||
--debug)
|
||||
DEBUG=1;;
|
||||
--os-packages-only)
|
||||
OS_PACKAGES_ONLY=1;;
|
||||
--no-self-upgrade)
|
||||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
|
|
@ -91,21 +97,18 @@ ExperimentalBootstrap() {
|
|||
}
|
||||
|
||||
DeterminePythonVersion() {
|
||||
if command -v python2.7 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python2.7}
|
||||
elif command -v python27 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python27}
|
||||
elif command -v python2 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python2}
|
||||
elif command -v python > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python}
|
||||
else
|
||||
echo "Cannot find any Pythons... please install one!"
|
||||
for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
|
||||
# Break (while keeping the LE_PYTHON value) if found.
|
||||
command -v "$LE_PYTHON" > /dev/null && break
|
||||
done
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Cannot find any Pythons; please install one!"
|
||||
exit 1
|
||||
fi
|
||||
export LE_PYTHON
|
||||
|
||||
PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
||||
if [ $PYVER -lt 26 ]; then
|
||||
PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
||||
if [ "$PYVER" -lt 26 ]; then
|
||||
echo "You have an ancient version of Python entombed in your operating system..."
|
||||
echo "This isn't going to work; you'll need at least version 2.6."
|
||||
exit 1
|
||||
|
|
@ -324,13 +327,13 @@ BootstrapGentooCommon() {
|
|||
|
||||
case "$PACKAGE_MANAGER" in
|
||||
(paludis)
|
||||
"$SUDO" cave resolve --keep-targets if-possible $PACKAGES -x
|
||||
"$SUDO" cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x
|
||||
;;
|
||||
(pkgcore)
|
||||
"$SUDO" pmerge --noreplace $PACKAGES
|
||||
"$SUDO" pmerge --noreplace --oneshot $PACKAGES
|
||||
;;
|
||||
(portage|*)
|
||||
"$SUDO" emerge --noreplace $PACKAGES
|
||||
"$SUDO" emerge --noreplace --oneshot $PACKAGES
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
|
@ -345,20 +348,27 @@ BootstrapFreeBsd() {
|
|||
|
||||
BootstrapMac() {
|
||||
if ! hash brew 2>/dev/null; then
|
||||
echo "Homebrew Not Installed\nDownloading..."
|
||||
echo "Homebrew not installed.\nDownloading..."
|
||||
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
fi
|
||||
|
||||
brew install augeas
|
||||
brew install dialog
|
||||
if [ -z "$(brew list --versions augeas)" ]; then
|
||||
echo "augeas not installed.\nInstalling augeas from Homebrew..."
|
||||
brew install augeas
|
||||
fi
|
||||
|
||||
if [ -z "$(brew list --versions dialog)" ]; then
|
||||
echo "dialog not installed.\nInstalling dialog from Homebrew..."
|
||||
brew install dialog
|
||||
fi
|
||||
|
||||
if ! hash pip 2>/dev/null; then
|
||||
echo "pip Not Installed\nInstalling python from Homebrew..."
|
||||
echo "pip not installed.\nInstalling python from Homebrew..."
|
||||
brew install python
|
||||
fi
|
||||
|
||||
if ! hash virtualenv 2>/dev/null; then
|
||||
echo "virtualenv Not Installed\nInstalling with pip"
|
||||
echo "virtualenv not installed.\nInstalling with pip..."
|
||||
pip install virtualenv
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh".
|
|||
XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
||||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN=${VENV_PATH}/bin
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
# This first clause is redundant with the third, but hedging on portability
|
||||
if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then
|
||||
VERBOSE=1
|
||||
elif [ "$arg" = "--no-self-upgrade" ] ; then
|
||||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1
|
||||
elif [ "$arg" = "--os-packages-only" ] ; then
|
||||
OS_PACKAGES_ONLY=1
|
||||
elif [ "$arg" = "--debug" ]; then
|
||||
DEBUG=1
|
||||
fi
|
||||
case "$arg" in
|
||||
--debug)
|
||||
DEBUG=1;;
|
||||
--os-packages-only)
|
||||
OS_PACKAGES_ONLY=1;;
|
||||
--no-self-upgrade)
|
||||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
|
|
@ -91,21 +97,18 @@ ExperimentalBootstrap() {
|
|||
}
|
||||
|
||||
DeterminePythonVersion() {
|
||||
if command -v python2.7 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python2.7}
|
||||
elif command -v python27 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python27}
|
||||
elif command -v python2 > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python2}
|
||||
elif command -v python > /dev/null ; then
|
||||
export LE_PYTHON=${LE_PYTHON:-python}
|
||||
else
|
||||
echo "Cannot find any Pythons... please install one!"
|
||||
for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
|
||||
# Break (while keeping the LE_PYTHON value) if found.
|
||||
command -v "$LE_PYTHON" > /dev/null && break
|
||||
done
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Cannot find any Pythons; please install one!"
|
||||
exit 1
|
||||
fi
|
||||
export LE_PYTHON
|
||||
|
||||
PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
||||
if [ $PYVER -lt 26 ]; then
|
||||
PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
||||
if [ "$PYVER" -lt 26 ]; then
|
||||
echo "You have an ancient version of Python entombed in your operating system..."
|
||||
echo "This isn't going to work; you'll need at least version 2.6."
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
BootstrapMac() {
|
||||
if ! hash brew 2>/dev/null; then
|
||||
echo "Homebrew Not Installed\nDownloading..."
|
||||
echo "Homebrew not installed.\nDownloading..."
|
||||
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
fi
|
||||
|
||||
brew install augeas
|
||||
brew install dialog
|
||||
if [ -z "$(brew list --versions augeas)" ]; then
|
||||
echo "augeas not installed.\nInstalling augeas from Homebrew..."
|
||||
brew install augeas
|
||||
fi
|
||||
|
||||
if [ -z "$(brew list --versions dialog)" ]; then
|
||||
echo "dialog not installed.\nInstalling dialog from Homebrew..."
|
||||
brew install dialog
|
||||
fi
|
||||
|
||||
if ! hash pip 2>/dev/null; then
|
||||
echo "pip Not Installed\nInstalling python from Homebrew..."
|
||||
echo "pip not installed.\nInstalling python from Homebrew..."
|
||||
brew install python
|
||||
fi
|
||||
|
||||
if ! hash virtualenv 2>/dev/null; then
|
||||
echo "virtualenv Not Installed\nInstalling with pip"
|
||||
echo "virtualenv not installed.\nInstalling with pip..."
|
||||
pip install virtualenv
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,12 +319,11 @@ def _handle_identical_cert_request(config, cert):
|
|||
elif config.verb == "certonly":
|
||||
keep_opt = "Keep the existing certificate for now"
|
||||
choices = [keep_opt,
|
||||
"Renew & replace the cert (limit ~5 per 7 days)",
|
||||
"Cancel this operation and do nothing"]
|
||||
"Renew & replace the cert (limit ~5 per 7 days)"]
|
||||
|
||||
display = zope.component.getUtility(interfaces.IDisplay)
|
||||
response = display.menu(question, choices, "OK", "Cancel", default=0)
|
||||
if response[0] == "cancel" or response[1] == 2:
|
||||
if response[0] == display_util.CANCEL:
|
||||
# TODO: Add notification related to command-line options for
|
||||
# skipping the menu for this case.
|
||||
raise errors.Error(
|
||||
|
|
|
|||
|
|
@ -112,6 +112,21 @@ def update_configuration(lineagename, target, cli_config):
|
|||
return configobj.ConfigObj(config_filename)
|
||||
|
||||
|
||||
def get_link_target(link):
|
||||
"""Get an absolute path to the target of link.
|
||||
|
||||
:param str link: Path to a symbolic link
|
||||
|
||||
:returns: Absolute path to the target of link
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
target = os.readlink(link)
|
||||
if not os.path.isabs(target):
|
||||
target = os.path.join(os.path.dirname(link), target)
|
||||
return os.path.abspath(target)
|
||||
|
||||
|
||||
class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
||||
"""Renewable certificate.
|
||||
|
||||
|
|
@ -194,13 +209,15 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
|||
|
||||
def _check_symlinks(self):
|
||||
"""Raises an exception if a symlink doesn't exist"""
|
||||
def check(link):
|
||||
"""Checks if symlink points to a file that exists"""
|
||||
return os.path.exists(os.path.realpath(link))
|
||||
for kind in ALL_FOUR:
|
||||
if not check(getattr(self, kind)):
|
||||
link = getattr(self, kind)
|
||||
if not os.path.islink(link):
|
||||
raise errors.CertStorageError(
|
||||
"link: {0} does not exist".format(getattr(self, kind)))
|
||||
"expected {0} to be a symlink".format(link))
|
||||
target = get_link_target(link)
|
||||
if not os.path.exists(target):
|
||||
raise errors.CertStorageError("target {0} of symlink {1} does "
|
||||
"not exist".format(target, link))
|
||||
|
||||
def _consistent(self):
|
||||
"""Are the files associated with this lineage self-consistent?
|
||||
|
|
@ -225,10 +242,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
|||
return False
|
||||
for kind in ALL_FOUR:
|
||||
link = getattr(self, kind)
|
||||
where = os.path.dirname(link)
|
||||
target = os.readlink(link)
|
||||
if not os.path.isabs(target):
|
||||
target = os.path.join(where, target)
|
||||
target = get_link_target(link)
|
||||
|
||||
# Each element's link must point within the cert lineage's
|
||||
# directory within the official archive directory
|
||||
|
|
@ -343,10 +357,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
|||
logger.debug("Expected symlink %s for %s does not exist.",
|
||||
link, kind)
|
||||
return None
|
||||
target = os.readlink(link)
|
||||
if not os.path.isabs(target):
|
||||
target = os.path.join(os.path.dirname(link), target)
|
||||
return os.path.abspath(target)
|
||||
return get_link_target(link)
|
||||
|
||||
def current_version(self, kind):
|
||||
"""Returns numerical version of the specified item.
|
||||
|
|
|
|||
|
|
@ -687,6 +687,10 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert,
|
||||
self.config.filename, self.cli_config)
|
||||
os.symlink("missing", self.config[ALL_FOUR[0]])
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert,
|
||||
self.config.filename, self.cli_config)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
[easy_install]
|
||||
zip_ok = false
|
||||
|
||||
[aliases]
|
||||
dev = develop easy_install letsencrypt[dev,docs,testing]
|
||||
|
||||
[nosetests]
|
||||
nocapture=1
|
||||
cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx
|
||||
|
|
|
|||
14
setup.py
14
setup.py
|
|
@ -65,7 +65,12 @@ else:
|
|||
dev_extras = [
|
||||
# Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289
|
||||
'astroid==1.3.5',
|
||||
'coverage',
|
||||
'nose',
|
||||
'nosexcover',
|
||||
'pep8',
|
||||
'pylint==1.4.2', # upstream #248
|
||||
'tox',
|
||||
'twine',
|
||||
'wheel',
|
||||
]
|
||||
|
|
@ -77,14 +82,6 @@ docs_extras = [
|
|||
'sphinxcontrib-programoutput',
|
||||
]
|
||||
|
||||
testing_extras = [
|
||||
'coverage',
|
||||
'nose',
|
||||
'nosexcover',
|
||||
'pep8',
|
||||
'tox',
|
||||
]
|
||||
|
||||
setup(
|
||||
name='letsencrypt',
|
||||
version=version,
|
||||
|
|
@ -120,7 +117,6 @@ setup(
|
|||
extras_require={
|
||||
'dev': dev_extras,
|
||||
'docs': docs_extras,
|
||||
'testing': testing_extras,
|
||||
},
|
||||
|
||||
# to test all packages run "python setup.py test -s
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
export VENV_ARGS="--python python2"
|
||||
|
||||
./tools/_venv_common.sh \
|
||||
-e acme[testing] \
|
||||
-e .[dev,docs,testing] \
|
||||
-e acme[dev] \
|
||||
-e .[dev,docs] \
|
||||
-e letsencrypt-apache \
|
||||
-e letsencrypt-nginx \
|
||||
-e letshelp-letsencrypt \
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ export VENV_NAME="${VENV_NAME:-venv3}"
|
|||
export VENV_ARGS="--python python3"
|
||||
|
||||
./tools/_venv_common.sh \
|
||||
-e acme[testing] \
|
||||
-e acme[dev] \
|
||||
|
|
|
|||
14
tox.ini
14
tox.ini
|
|
@ -15,9 +15,9 @@ envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint
|
|||
# packages installed separately to ensure that downstream deps problems
|
||||
# are detected, c.f. #1002
|
||||
commands =
|
||||
pip install -e acme[testing]
|
||||
pip install -e acme[dev]
|
||||
nosetests -v acme
|
||||
pip install -e .[testing]
|
||||
pip install -e .[dev]
|
||||
nosetests -v letsencrypt
|
||||
pip install -e letsencrypt-apache
|
||||
nosetests -v letsencrypt_apache
|
||||
|
|
@ -40,23 +40,23 @@ deps =
|
|||
|
||||
[testenv:py33]
|
||||
commands =
|
||||
pip install -e acme[testing]
|
||||
pip install -e acme[dev]
|
||||
nosetests -v acme
|
||||
|
||||
[testenv:py34]
|
||||
commands =
|
||||
pip install -e acme[testing]
|
||||
pip install -e acme[dev]
|
||||
nosetests -v acme
|
||||
|
||||
[testenv:py35]
|
||||
commands =
|
||||
pip install -e acme[testing]
|
||||
pip install -e acme[dev]
|
||||
nosetests -v acme
|
||||
|
||||
[testenv:cover]
|
||||
basepython = python2.7
|
||||
commands =
|
||||
pip install -e acme -e .[testing] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt
|
||||
pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt
|
||||
./tox.cover.sh
|
||||
|
||||
[testenv:lint]
|
||||
|
|
@ -66,7 +66,7 @@ basepython = python2.7
|
|||
# duplicate code checking; if one of the commands fails, others will
|
||||
# continue, but tox return code will reflect previous error
|
||||
commands =
|
||||
pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt
|
||||
pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt
|
||||
./pep8.travis.sh
|
||||
pylint --rcfile=.pylintrc letsencrypt
|
||||
pylint --rcfile=acme/.pylintrc acme/acme
|
||||
|
|
|
|||
Loading…
Reference in a new issue