From d06c6f27bdfbde31e18d5a5f3fc40c0a85b723e7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 16 Dec 2015 14:33:41 -0800 Subject: [PATCH 01/15] Removed duplicate cancel --- letsencrypt/cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 29519d430..6634b422d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -276,12 +276,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") - 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( From a04b92eb8e91e6f51b3a851313e16821057c9edf Mon Sep 17 00:00:00 2001 From: asaph Date: Tue, 22 Dec 2015 13:37:36 -0800 Subject: [PATCH 02/15] OSX: check if augeas, dialog are already installed This check avoids the following 2 noisy warnings: Warning: augeas-1.4.0 already installed Warning: dialog-1.2-20150528 already installed --- bootstrap/mac.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bootstrap/mac.sh b/bootstrap/mac.sh index 4d1fb8208..4b6612336 100755 --- a/bootstrap/mac.sh +++ b/bootstrap/mac.sh @@ -4,8 +4,15 @@ if ! hash brew 2>/dev/null; then 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..." From 31a64a0e9ffe2d5cbbfc0f6eb87329ae8c75f007 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Jan 2016 18:01:58 +0000 Subject: [PATCH 03/15] ACME: default to new_authzr_uri form Directory --- acme/acme/client.py | 21 +++++++++++---------- acme/acme/client_test.py | 32 ++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index c3e28ef47..b8d30461d 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -181,40 +181,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. diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 58f55b293..db5dcd6ed 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -36,6 +36,7 @@ class ClientTest(unittest.TestCase): self.directory = messages.Directory({ messages.NewRegistration: '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 @@ -133,7 +134,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() @@ -141,10 +142,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( @@ -153,15 +164,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} From 20ab48820aec5762861867422124d7b4df4ba141 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Jan 2016 18:36:24 +0000 Subject: [PATCH 04/15] Remove unused `setup.py dev` alias --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index ca4c1b1ca..1ea06661e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 From 86d6d2704511819a2974334ae93313229d3cb7d1 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Jan 2016 18:37:41 +0000 Subject: [PATCH 05/15] Clean up dev/testing extras messup (fixes #2140). --- Dockerfile-dev | 2 +- acme/setup.py | 13 +++++++------ bootstrap/dev/venv.sh | 4 ++-- bootstrap/dev/venv3.sh | 2 +- setup.py | 14 +++++--------- tox.ini | 14 +++++++------- 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/Dockerfile-dev b/Dockerfile-dev index 3c5b53966..0e9e62486 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -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); diff --git a/acme/setup.py b/acme/setup.py index 372c05b13..e7371e415 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -36,17 +36,18 @@ if sys.version_info < (2, 7, 9): install_requires.append('ndg-httpsclient') install_requires.append('pyasn1') +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', @@ -76,8 +77,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': [ diff --git a/bootstrap/dev/venv.sh b/bootstrap/dev/venv.sh index 11ab417dd..eea79e846 100755 --- a/bootstrap/dev/venv.sh +++ b/bootstrap/dev/venv.sh @@ -4,8 +4,8 @@ export VENV_ARGS="--python python2" ./bootstrap/dev/_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 \ diff --git a/bootstrap/dev/venv3.sh b/bootstrap/dev/venv3.sh index ccffffb83..f1d2b581a 100755 --- a/bootstrap/dev/venv3.sh +++ b/bootstrap/dev/venv3.sh @@ -5,4 +5,4 @@ export VENV_NAME="${VENV_NAME:-venv3}" export VENV_ARGS="--python python3" ./bootstrap/dev/_venv_common.sh \ - -e acme[testing] \ + -e acme[dev] \ diff --git a/setup.py b/setup.py index ad7fb6909..370a9cdb5 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,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', ] @@ -76,14 +81,6 @@ docs_extras = [ 'sphinxcontrib-programoutput', ] -testing_extras = [ - 'coverage', - 'nose', - 'nosexcover', - 'pep8', - 'tox', -] - setup( name='letsencrypt', version=version, @@ -119,7 +116,6 @@ setup( extras_require={ 'dev': dev_extras, 'docs': docs_extras, - 'testing': testing_extras, }, tests_require=install_requires, diff --git a/tox.ini b/tox.ini index c6cefb764..6b05efabd 100644 --- a/tox.ini +++ b/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 dowstream 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 From e2a6bdf574d05fde3957a9eced067678051ac3b5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 10 Feb 2016 20:05:34 -0800 Subject: [PATCH 06/15] letstest: work with a local repo, even if it isn't called letsencrypt * allows test farm tests to be run from release branches --- tests/letstest/multitester.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 378670071..e27385002 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -241,21 +241,21 @@ def local_git_clone(repo_url): "clones master of repo_url" with lcd(LOGDIR): local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') - local('git clone %s'% repo_url) + local('git clone %s letsencrypt'% repo_url) local('tar czf le.tar.gz letsencrypt') def local_git_branch(repo_url, branch_name): "clones branch of repo_url" with lcd(LOGDIR): local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') - local('git clone %s --branch %s --single-branch'%(repo_url, branch_name)) + local('git clone %s letsencrypt --branch %s --single-branch'%(repo_url, branch_name)) local('tar czf le.tar.gz letsencrypt') def local_git_PR(repo_url, PRnumstr, merge_master=True): "clones specified pull request from repo_url and optionally merges into master" with lcd(LOGDIR): local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') - local('git clone %s'% repo_url) + local('git clone %s letsencrypt'% repo_url) local('cd letsencrypt && git fetch origin pull/%s/head:lePRtest'%PRnumstr) local('cd letsencrypt && git co lePRtest') if merge_master: From a43a21d4e250a8df293b81b73949ae7cf2785d11 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 11 Feb 2016 15:37:17 -0800 Subject: [PATCH 07/15] Use example domains in README --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 57908e90f..522400a1f 100644 --- a/README.rst +++ b/README.rst @@ -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 From 52eee4fbfb3c583a59b9082e368b9b4f87e52f72 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 11 Feb 2016 15:45:26 -0800 Subject: [PATCH 08/15] Use example domains in using.rst --- docs/using.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 9ee16dffd..15c3df869 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -118,11 +118,11 @@ directory of the files served by your webserver. For example, If you're getting a certificate for many domains at once, each domain will use the most recent ``--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 From e1e52a9d56f684cf2cb289a0661da65c132843c7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 11 Feb 2016 17:47:15 -0800 Subject: [PATCH 09/15] Verify both symlink and target --- letsencrypt/storage.py | 37 ++++++++++++++++++++----------- letsencrypt/tests/storage_test.py | 4 ++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 6f7f54646..6786ac745 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -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. diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 9d402089c..071d6d7bb 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -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__": From c80535b2dfadfb9d700cb03a1d5c8735332567d6 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 12 Feb 2016 09:49:17 +0100 Subject: [PATCH 10/15] Fixed typo in Webroot section --- docs/using.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 7263452b5..be877b17e 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -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 # -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. From cedcad137391e277552b9a8a5dbc636b2f081b77 Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 12 Feb 2016 11:49:01 -0500 Subject: [PATCH 11/15] Use python -V instead of python --version. Fix #2039. Python 2.4 doesn't support --version, and we want to be able to at least complain that it's too old without crashing. Also, bring built le-auto up to date. --- letsencrypt-auto-source/letsencrypt-auto | 8 ++++---- letsencrypt-auto-source/letsencrypt-auto.template | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 30d907690..4ee16af95 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -104,7 +104,7 @@ DeterminePythonVersion() { exit 1 fi - PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + 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." @@ -324,13 +324,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 } diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 1fa12528f..b65b55163 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -104,7 +104,7 @@ DeterminePythonVersion() { exit 1 fi - PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + 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." From dc8bdfac565d0fbb6920a550ee758000b39cd8dc Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 12 Feb 2016 15:07:01 -0500 Subject: [PATCH 12/15] Quote the remaining variable expansions in le-auto. Refs #1899. ...except for $SUDO, which is always either "sudo", "su_sudo", or "", never having a quote-needing char in it. It's unlikely that $PYVER would have a space in it, but it doesn't hurt. --- letsencrypt-auto-source/letsencrypt-auto | 4 ++-- letsencrypt-auto-source/letsencrypt-auto.template | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index ea3f7a4bb..420e6263b 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -18,7 +18,7 @@ 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 @@ -105,7 +105,7 @@ DeterminePythonVersion() { fi PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` - if [ $PYVER -lt 26 ]; then + 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 diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index b65b55163..042a448f5 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -18,7 +18,7 @@ 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 @@ -105,7 +105,7 @@ DeterminePythonVersion() { fi PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` - if [ $PYVER -lt 26 ]; then + 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 From 4c2c80dcdaf78ebdd44185762484f9db9ff147b8 Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 12 Feb 2016 17:23:29 -0500 Subject: [PATCH 13/15] Fix DeterminePythonVersion(). Ported from #1751. * Make sure any Python passed in as $LE_PYTHON actually exists. * Dodge a word-splitting bug: `a='a b'; export a=${a:-c}; echo $a` gives `a` instead of `a b` under shells that respect POSIX.1, like dash. --- letsencrypt-auto-source/letsencrypt-auto | 17 +++++++---------- .../letsencrypt-auto.template | 17 +++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index ea3f7a4bb..bfb41e240 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -91,18 +91,15 @@ 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" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` if [ $PYVER -lt 26 ]; then diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index b65b55163..2572e13d6 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -91,18 +91,15 @@ 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" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` if [ $PYVER -lt 26 ]; then From e08aa36a4ea5d4a1d60364f544e733a8e79fe97a Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 12 Feb 2016 17:36:48 -0500 Subject: [PATCH 14/15] Switch to case statement for arg parsing in le-auto. Ported from #1751. * It's more lines but fewer tokens, less room for quote errors, and more idiomatic (see any init.d script). * Also, fix a bug in which any option containing "-v", e.g. --eat-vertical-pizza, would be construed as --verbose. --- letsencrypt-auto-source/letsencrypt-auto | 30 +++++++++++-------- .../letsencrypt-auto.template | 30 +++++++++++-------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index bfb41e240..1837120e8 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -25,18 +25,24 @@ LE_AUTO_VERSION="0.5.0.dev0" # 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 diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 2572e13d6..f0e87cf14 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -25,18 +25,24 @@ LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" # 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 From 42a61537e6f34b40b458aae44e1a138b8e0d555e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 12 Feb 2016 14:48:14 -0800 Subject: [PATCH 15/15] Remove unecessary check and use constant --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index bfedea15a..fcc6d3bd5 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -323,7 +323,7 @@ def _handle_identical_cert_request(config, cert): 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(