diff --git a/.gitignore b/.gitignore index 8118edfd4..b653cb06c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist*/ /.tox/ /releases/ letsencrypt.log +certbot.log letsencrypt-auto-source/letsencrypt-auto.sig.lzma.base64 # coverage diff --git a/.travis.yml b/.travis.yml index 6b325e985..5d70ca799 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,15 +28,31 @@ matrix: include: - python: "2.6" env: TOXENV=py26 BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.6" env: TOXENV=py26-oldest BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=apacheconftest sudo: required - python: "2.7" env: TOXENV=py27 BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=py27-oldest BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=lint - sudo: required @@ -66,13 +82,16 @@ sudo: false addons: # Custom /etc/hosts required for simple verification of http-01 - # and tls-sni-01, and for letsencrypt_test_nginx + # and tls-sni-01, and for certbot_test_nginx hosts: - le.wtf - le1.wtf - le2.wtf - le3.wtf - nginx.wtf + - boulder + - boulder-mysql + - boulder-rabbitmq mariadb: "10.0" apt: sources: @@ -86,13 +105,12 @@ addons: - libssl-dev - libffi-dev - ca-certificates - # For letsencrypt-nginx integration testing + # For certbot-nginx integration testing - nginx-light - openssl # For Boulder integration testing - rsyslog # for apacheconftest - #- realpath #- apache2 #- libapache2-mod-wsgi #- libapache2-mod-macro diff --git a/Dockerfile b/Dockerfile index ccbb07b95..3e4c9430e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,11 @@ MAINTAINER William Budington EXPOSE 443 # TODO: make sure --config-dir and --work-dir cannot be changed -# through the CLI (letsencrypt-docker wrapper that uses standalone +# through the CLI (certbot-docker wrapper that uses standalone # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt -WORKDIR /opt/letsencrypt +WORKDIR /opt/certbot # no need to mkdir anything: # https://docs.docker.com/reference/builder/#copy @@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt # directories in its path. -COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto -RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ +COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto +RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* \ /tmp/* \ @@ -33,7 +33,7 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages- # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/ # all above files are necessary for setup.py and venv setup, however, # package source code directory has to be copied separately to a @@ -44,26 +44,26 @@ COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/ # copied, just its contents." Order again matters, three files are far # more likely to be cached than the whole project directory -COPY letsencrypt /opt/letsencrypt/src/letsencrypt/ -COPY acme /opt/letsencrypt/src/acme/ -COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ -COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ +COPY certbot /opt/certbot/src/certbot/ +COPY acme /opt/certbot/src/acme/ +COPY certbot-apache /opt/certbot/src/certbot-apache/ +COPY certbot-nginx /opt/certbot/src/certbot-nginx/ -RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv +RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv # PATH is set now so pipstrap upgrades the correct (v)env -ENV PATH /opt/letsencrypt/venv/bin:$PATH -RUN /opt/letsencrypt/venv/bin/python /opt/letsencrypt/src/pipstrap.py && \ - /opt/letsencrypt/venv/bin/pip install \ - -e /opt/letsencrypt/src/acme \ - -e /opt/letsencrypt/src \ - -e /opt/letsencrypt/src/letsencrypt-apache \ - -e /opt/letsencrypt/src/letsencrypt-nginx +ENV PATH /opt/certbot/venv/bin:$PATH +RUN /opt/certbot/venv/bin/python /opt/certbot/src/pipstrap.py && \ + /opt/certbot/venv/bin/pip install \ + -e /opt/certbot/src/acme \ + -e /opt/certbot/src \ + -e /opt/certbot/src/certbot-apache \ + -e /opt/certbot/src/certbot-nginx # 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); +# "rm -rf /opt/certbot/src" (it's stays in the underlaying image); # this might also help in debugging: you can "docker run --entrypoint # bash" and investigate, apply patches, etc. -ENTRYPOINT [ "letsencrypt" ] +ENTRYPOINT [ "certbot" ] diff --git a/Dockerfile-dev b/Dockerfile-dev index 56e2ec05b..c7e1d7b2e 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -9,11 +9,11 @@ MAINTAINER Yan EXPOSE 443 # TODO: make sure --config-dir and --work-dir cannot be changed -# through the CLI (letsencrypt-docker wrapper that uses standalone +# through the CLI (certbot-docker wrapper that uses standalone # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt -WORKDIR /opt/letsencrypt +WORKDIR /opt/certbot # no need to mkdir anything: # https://docs.docker.com/reference/builder/#copy @@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt # TODO: Install non-default Python versions for tox. # TODO: Install Apache/Nginx for plugin development. -COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto -RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ +COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto +RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* \ /tmp/* \ @@ -32,7 +32,7 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages- # the above is not likely to change, so by putting it further up the # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/certbot/src/ # all above files are necessary for setup.py, however, package source # code directory has to be copied separately to a subdirectory... @@ -42,27 +42,27 @@ COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh t # copied, just its contents." Order again matters, three files are far # more likely to be cached than the whole project directory -COPY letsencrypt /opt/letsencrypt/src/letsencrypt/ -COPY acme /opt/letsencrypt/src/acme/ -COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ -COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ -COPY letshelp-letsencrypt /opt/letsencrypt/src/letshelp-letsencrypt/ -COPY letsencrypt-compatibility-test /opt/letsencrypt/src/letsencrypt-compatibility-test/ -COPY tests /opt/letsencrypt/src/tests/ +COPY certbot /opt/certbot/src/certbot/ +COPY acme /opt/certbot/src/acme/ +COPY certbot-apache /opt/certbot/src/certbot-apache/ +COPY certbot-nginx /opt/certbot/src/certbot-nginx/ +COPY letshelp-certbot /opt/certbot/src/letshelp-certbot/ +COPY certbot-compatibility-test /opt/certbot/src/certbot-compatibility-test/ +COPY tests /opt/certbot/src/tests/ -RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ - /opt/letsencrypt/venv/bin/pip install \ - -e /opt/letsencrypt/src/acme \ - -e /opt/letsencrypt/src \ - -e /opt/letsencrypt/src/letsencrypt-apache \ - -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] +RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv && \ + /opt/certbot/venv/bin/pip install \ + -e /opt/certbot/src/acme \ + -e /opt/certbot/src \ + -e /opt/certbot/src/certbot-apache \ + -e /opt/certbot/src/certbot-nginx \ + -e /opt/certbot/src/letshelp-certbot \ + -e /opt/certbot/src/certbot-compatibility-test \ + -e /opt/certbot/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); +# "rm -rf /opt/certbot/src" (it's stays in the underlaying image); # this might also help in debugging: you can "docker run --entrypoint # bash" and investigate, apply patches, etc. -ENV PATH /opt/letsencrypt/venv/bin:$PATH +ENV PATH /opt/certbot/venv/bin:$PATH diff --git a/LICENSE.txt b/LICENSE.txt index 5965ec2ef..b905dd120 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Let's Encrypt Python Client +Certbot ACME Client Copyright (c) Electronic Frontier Foundation and others Licensed Apache Version 2.0 diff --git a/MANIFEST.in b/MANIFEST.in index a6f9ae2b6..18393e3e1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,4 +5,4 @@ include LICENSE.txt include linter_plugin.py recursive-include docs * recursive-include examples * -recursive-include letsencrypt/tests/testdata * +recursive-include certbot/tests/testdata * diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 06b4492d6..24a3b580c 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -123,6 +123,12 @@ class Directory(jose.JSONDeSerializable): _REGISTERED_TYPES = {} + class Meta(jose.JSONObjectWithFields): + """Directory Meta.""" + terms_of_service = jose.Field('terms-of-service', omitempty=True) + website = jose.Field('website', omitempty=True) + caa_identities = jose.Field('caa-identities', omitempty=True) + @classmethod def _canon_key(cls, key): return getattr(key, 'resource_type', key) @@ -137,10 +143,11 @@ class Directory(jose.JSONDeSerializable): def __init__(self, jobj): canon_jobj = util.map_keys(jobj, self._canon_key) - if not set(canon_jobj).issubset(self._REGISTERED_TYPES): + if not set(canon_jobj).issubset( + set(self._REGISTERED_TYPES).union(['meta'])): # TODO: acme-spec is not clear about this: 'It is a JSON # dictionary, whose keys are the "resource" values listed - # in {{https-requests}}'z + # in {{https-requests}}' raise ValueError('Wrong directory fields') # TODO: check that everything is an absolute URL; acme-spec is # not clear on that @@ -163,6 +170,7 @@ class Directory(jose.JSONDeSerializable): @classmethod def from_json(cls, jobj): + jobj['meta'] = cls.Meta.from_json(jobj.pop('meta', {})) try: return cls(jobj) except ValueError as error: diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index fa558cf4a..b2b7febdc 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -90,6 +90,11 @@ class DirectoryTest(unittest.TestCase): self.dir = Directory({ 'new-reg': 'reg', mock.MagicMock(resource_type='new-cert'): 'cert', + 'meta': Directory.Meta( + terms_of_service='https://example.com/acme/terms', + website='https://www.example.com/', + caa_identities=['example.com'], + ), }) def test_init_wrong_key_value_error(self): @@ -111,9 +116,16 @@ class DirectoryTest(unittest.TestCase): def test_getattr_fails_with_attribute_error(self): self.assertRaises(AttributeError, self.dir.__getattr__, 'foo') - def test_to_partial_json(self): - self.assertEqual( - self.dir.to_partial_json(), {'new-reg': 'reg', 'new-cert': 'cert'}) + def test_to_json(self): + self.assertEqual(self.dir.to_json(), { + 'new-reg': 'reg', + 'new-cert': 'cert', + 'meta': { + 'terms-of-service': 'https://example.com/acme/terms', + 'website': 'https://www.example.com/', + 'caa-identities': ['example.com'], + }, + }) def test_from_json_deserialization_error_on_wrong_key(self): from acme.messages import Directory diff --git a/acme/setup.py b/acme/setup.py index 0843288e6..cbd3bfb87 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ @@ -53,7 +53,7 @@ setup( version=version, description='ACME protocol implementation in Python', url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/letsencrypt-compatibility-test/LICENSE.txt b/certbot-apache/LICENSE.txt similarity index 100% rename from letsencrypt-compatibility-test/LICENSE.txt rename to certbot-apache/LICENSE.txt diff --git a/certbot-apache/MANIFEST.in b/certbot-apache/MANIFEST.in new file mode 100644 index 000000000..3e594a953 --- /dev/null +++ b/certbot-apache/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include certbot_apache/tests/testdata * +include certbot_apache/centos-options-ssl-apache.conf +include certbot_apache/options-ssl-apache.conf +recursive-include certbot_apache/augeas_lens *.aug diff --git a/certbot-apache/README.rst b/certbot-apache/README.rst new file mode 100644 index 000000000..96a6ff8ae --- /dev/null +++ b/certbot-apache/README.rst @@ -0,0 +1 @@ +Apache plugin for Certbot diff --git a/certbot-apache/certbot_apache/__init__.py b/certbot-apache/certbot_apache/__init__.py new file mode 100644 index 000000000..9c195ccc7 --- /dev/null +++ b/certbot-apache/certbot_apache/__init__.py @@ -0,0 +1 @@ +"""Certbot Apache plugin.""" diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_configurator.py b/certbot-apache/certbot_apache/augeas_configurator.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/augeas_configurator.py rename to certbot-apache/certbot_apache/augeas_configurator.py index 9b51c32a9..12753541c 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_configurator.py +++ b/certbot-apache/certbot_apache/augeas_configurator.py @@ -3,11 +3,11 @@ import logging import augeas -from letsencrypt import errors -from letsencrypt import reverter -from letsencrypt.plugins import common +from certbot import errors +from certbot import reverter +from certbot.plugins import common -from letsencrypt_apache import constants +from certbot_apache import constants logger = logging.getLogger(__name__) @@ -16,14 +16,14 @@ class AugeasConfigurator(common.Plugin): """Base Augeas Configurator class. :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar aug: Augeas object :type aug: :class:`augeas.Augeas` :ivar str save_notes: Human-readable configuration change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.reverter.Reverter` + :type reverter: :class:`certbot.reverter.Reverter` """ def __init__(self, *args, **kwargs): diff --git a/certbot-apache/certbot_apache/augeas_lens/README b/certbot-apache/certbot_apache/augeas_lens/README new file mode 100644 index 000000000..bf9161f93 --- /dev/null +++ b/certbot-apache/certbot_apache/augeas_lens/README @@ -0,0 +1,2 @@ +Certbot includes the very latest Augeas lenses in order to ship bug fixes +to Apache configuration handling bugs as quickly as possible diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/certbot-apache/certbot_apache/augeas_lens/httpd.aug similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug rename to certbot-apache/certbot_apache/augeas_lens/httpd.aug diff --git a/letsencrypt-apache/letsencrypt_apache/centos-options-ssl-apache.conf b/certbot-apache/certbot_apache/centos-options-ssl-apache.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/centos-options-ssl-apache.conf rename to certbot-apache/certbot_apache/centos-options-ssl-apache.conf diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py similarity index 94% rename from letsencrypt-apache/letsencrypt_apache/configurator.py rename to certbot-apache/certbot_apache/configurator.py index 3a679fa7e..26c3185be 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -13,18 +13,18 @@ import zope.interface from acme import challenges -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_apache import augeas_configurator -from letsencrypt_apache import constants -from letsencrypt_apache import display_ops -from letsencrypt_apache import tls_sni_01 -from letsencrypt_apache import obj -from letsencrypt_apache import parser +from certbot_apache import augeas_configurator +from certbot_apache import constants +from certbot_apache import display_ops +from certbot_apache import tls_sni_01 +from certbot_apache import obj +from certbot_apache import parser from collections import defaultdict @@ -70,14 +70,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): 14.04 Apache 2.4 and it works for Ubuntu 12.04 Apache 2.2 :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt_apache.parser` + :type parser: :class:`~certbot_apache.parser` :ivar tup version: version of Apache :ivar list vhosts: All vhosts found in the configuration - (:class:`list` of :class:`~letsencrypt_apache.obj.VirtualHost`) + (:class:`list` of :class:`~certbot_apache.obj.VirtualHost`) :ivar dict assoc: Mapping between domains and vhosts @@ -205,7 +205,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): installed, the VirtualHost is enabled if it isn't already. .. todo:: Might be nice to remove chain directive if none exists - This shouldn't happen within letsencrypt though + This shouldn't happen within certbot though :raises errors.PluginError: When unable to deploy certificate due to a lack of directives @@ -290,7 +290,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param bool temp: whether the vhost is only used temporarily :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.PluginError: If no vhost is available or chosen @@ -345,7 +345,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def included_in_wildcard(self, names, target_name): """Helper function to see if alias is covered by wildcard""" target_name = target_name.split(".")[::-1] - wildcards = [domain.split(".")[1:] for domain in names if domain.startswith("*")] + wildcards = [domain.split(".")[1:] for domain in + names if domain.startswith("*")] for wildcard in wildcards: if len(wildcard) > len(target_name): continue @@ -471,7 +472,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Helper function for get_virtual_hosts(). :param host: In progress vhost whose names will be added - :type host: :class:`~letsencrypt_apache.obj.VirtualHost` + :type host: :class:`~certbot_apache.obj.VirtualHost` """ # Take the final ServerName as each overrides the previous @@ -497,7 +498,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str path: Augeas path to virtual host :returns: newly created vhost - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` """ addrs = set() @@ -533,7 +534,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def get_virtual_hosts(self): """Returns list of virtual hosts found in the Apache configuration. - :returns: List of :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: List of :class:`~certbot_apache.obj.VirtualHost` objects found in configuration :rtype: list @@ -545,7 +546,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): paths = self.aug.match( ("/files%s//*[label()=~regexp('%s')]" % (vhost_path, parser.case_i("VirtualHost")))) - paths = [path for path in paths if os.path.basename(path) == "VirtualHost"] + paths = [path for path in paths if + os.path.basename(path) == "VirtualHost"] for path in paths: new_vhost = self._create_vhost(path) realpath = os.path.realpath(new_vhost.filep) @@ -570,7 +572,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): now NameVirtualHosts. If version is earlier than 2.4, check if addr has a NameVirtualHost directive in the Apache config - :param letsencrypt_apache.obj.Addr target_addr: vhost address + :param certbot_apache.obj.Addr target_addr: vhost address :returns: Success :rtype: bool @@ -588,7 +590,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Adds NameVirtualHost directive for given address. :param addr: Address that will be added as NameVirtualHost directive - :type addr: :class:`~letsencrypt_apache.obj.Addr` + :type addr: :class:`~certbot_apache.obj.Addr` """ loc = parser.get_aug_path(self.parser.loc["name"]) @@ -677,7 +679,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks to see if the server is ready for SNI challenges. :param addrs: Addresses to check SNI compatibility - :type addrs: :class:`~letsencrypt_apache.obj.Addr` + :type addrs: :class:`~certbot_apache.obj.Addr` """ # Version 2.4 and later are automatically SNI ready. @@ -695,15 +697,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Duplicates vhost and adds default ssl options New vhost will reside as (nonssl_vhost.path) + - ``letsencrypt_apache.constants.os_constant("le_vhost_ext")`` + ``certbot_apache.constants.os_constant("le_vhost_ext")`` .. note:: This function saves the configuration :param nonssl_vhost: Valid VH that doesn't have SSLEngine on - :type nonssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type nonssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: SSL vhost - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.PluginError: If more than one virtual host is in the file or if plugin is unable to write/read vhost files. @@ -890,10 +892,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not vh_p: return vh_path = vh_p[0] - if (self.parser.find_dir("ServerName", target_name, start=vh_path, exclude=False) - or self.parser.find_dir("ServerAlias", target_name, start=vh_path, exclude=False)): + if (self.parser.find_dir("ServerName", target_name, + start=vh_path, exclude=False) or + self.parser.find_dir("ServerAlias", target_name, + start=vh_path, exclude=False)): return - if not self.parser.find_dir("ServerName", None, start=vh_path, exclude=False): + if not self.parser.find_dir("ServerName", None, + start=vh_path, exclude=False): self.parser.add_dir(vh_path, "ServerName", target_name) else: self.parser.add_dir(vh_path, "ServerAlias", target_name) @@ -906,7 +911,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): https://httpd.apache.org/docs/2.2/mod/core.html#namevirtualhost :param vhost: New virtual host that was recently created. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` """ need_to_save = False @@ -946,9 +951,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.constants.ENHANCEMENTS` + See :const:`~certbot.constants.ENHANCEMENTS` documentation for appropriate parameter. :raises .errors.PluginError: If Enhancement is not supported, or if @@ -976,14 +981,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param ssl_vhost: Destination of traffic, an ssl enabled vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :param header_substring: string that uniquely identifies a header. e.g: Strict-Transport-Security, Upgrade-Insecure-Requests. :type str :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) + :rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`) :raises .errors.PluginError: If no viable HTTP host can be created or set with header header_substring. @@ -1011,7 +1016,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): contains the string header_substring. :param ssl_vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :param header_substring: string that uniquely identifies a header. e.g: Strict-Transport-Security, Upgrade-Insecure-Requests. @@ -1048,13 +1053,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param ssl_vhost: Destination of traffic, an ssl enabled vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) + :rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`) :raises .errors.PluginError: If no viable HTTP host can be created or used for the redirect. @@ -1078,11 +1083,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "redirection") self._create_redirect_vhost(ssl_vhost) else: - # Check if LetsEncrypt redirection already exists - self._verify_no_letsencrypt_redirect(general_vh) + # Check if Certbot redirection already exists + self._verify_no_certbot_redirect(general_vh) # Note: if code flow gets here it means we didn't find the exact - # letsencrypt RewriteRule config for redirection. Finding + # certbot RewriteRule config for redirection. Finding # another RewriteRule is likely to be fine in most or all cases, # but redirect loops are possible in very obscure cases; see #1620 # for reasoning. @@ -1116,17 +1121,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): logger.info("Redirecting vhost in %s to ssl vhost in %s", general_vh.filep, ssl_vhost.filep) - def _verify_no_letsencrypt_redirect(self, vhost): - """Checks to see if a redirect was already installed by letsencrypt. + def _verify_no_certbot_redirect(self, vhost): + """Checks to see if a redirect was already installed by certbot. Checks to see if virtualhost already contains a rewrite rule that is - identical to Letsencrypt's redirection rewrite rule. + identical to Certbot's redirection rewrite rule. :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :raises errors.PluginEnhancementAlreadyPresent: When the exact - letsencrypt redirection WriteRule exists in virtual host. + certbot redirection WriteRule exists in virtual host. """ rewrite_path = self.parser.find_dir( "RewriteRule", None, start=vhost.path) @@ -1149,13 +1154,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for matches in rewrite_args_dict.values(): if [self.aug.get(x) for x in matches] in redirect_args: raise errors.PluginEnhancementAlreadyPresent( - "Let's Encrypt has already enabled redirection") + "Certbot has already enabled redirection") def _is_rewrite_exists(self, vhost): """Checks if there exists a RewriteRule directive in vhost :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: True if a RewriteRule directive exists. :rtype: bool @@ -1169,7 +1174,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks if a RewriteEngine directive is on :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` """ rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on", @@ -1182,10 +1187,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Creates an http_vhost specifically to redirect for the ssl_vhost. :param ssl_vhost: ssl vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: tuple of the form - (`success`, :class:`~letsencrypt_apache.obj.VirtualHost`) + (`success`, :class:`~certbot_apache.obj.VirtualHost`) :rtype: tuple """ @@ -1364,7 +1369,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. todo:: Make sure link is not broken... :param vhost: vhost to enable - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.NotSupportedError: If filesystem layout is not supported. @@ -1447,7 +1452,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not le_util.exe_exists(self.conf("dismod")): raise errors.MisconfigurationError( "Unable to find a2dismod, please make sure a2enmod and " - "a2dismod are configured correctly for letsencrypt.") + "a2dismod are configured correctly for certbot.") self.reverter.register_undo_command( temp, [self.conf("dismod"), mod_name]) @@ -1630,11 +1635,11 @@ def get_file_path(vhost_path): def install_ssl_options_conf(options_ssl): """ - Copy Let's Encrypt's SSL options file into the system's config dir if + Copy Certbot's SSL options file into the system's config dir if required. """ # XXX if we ever try to enforce a local privilege boundary (eg, running - # letsencrypt for unprivileged users via setuid), this function will need + # certbot for unprivileged users via setuid), this function will need # to be modified. # XXX if the user is in security-autoupdate mode, we should be willing to diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/certbot-apache/certbot_apache/constants.py similarity index 92% rename from letsencrypt-apache/letsencrypt_apache/constants.py rename to certbot-apache/certbot_apache/constants.py index ab85fb1f6..f3226572c 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/certbot-apache/certbot_apache/constants.py @@ -1,6 +1,6 @@ """Apache plugin constants.""" import pkg_resources -from letsencrypt import le_util +from certbot import le_util CLI_DEFAULTS_DEBIAN = dict( @@ -18,7 +18,7 @@ CLI_DEFAULTS_DEBIAN = dict( handle_sites=True, challenge_location="/etc/apache2", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", @@ -35,7 +35,7 @@ CLI_DEFAULTS_CENTOS = dict( handle_sites=False, challenge_location="/etc/httpd/conf.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "centos-options-ssl-apache.conf") + "certbot_apache", "centos-options-ssl-apache.conf") ) CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", @@ -52,7 +52,7 @@ CLI_DEFAULTS_GENTOO = dict( handle_sites=False, challenge_location="/etc/apache2/vhosts.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_DARWIN = dict( server_root="/etc/apache2", @@ -69,7 +69,7 @@ CLI_DEFAULTS_DARWIN = dict( handle_sites=False, challenge_location="/etc/apache2/other", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, @@ -87,7 +87,7 @@ MOD_SSL_CONF_DEST = "options-ssl-apache.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" AUGEAS_LENS_DIR = pkg_resources.resource_filename( - "letsencrypt_apache", "augeas_lens") + "certbot_apache", "augeas_lens") """Path to the Augeas lens directory""" REWRITE_HTTPS_ARGS = [ diff --git a/letsencrypt-apache/letsencrypt_apache/display_ops.py b/certbot-apache/certbot_apache/display_ops.py similarity index 94% rename from letsencrypt-apache/letsencrypt_apache/display_ops.py rename to certbot-apache/certbot_apache/display_ops.py index 4c01579cc..c9359e7d3 100644 --- a/letsencrypt-apache/letsencrypt_apache/display_ops.py +++ b/certbot-apache/certbot_apache/display_ops.py @@ -4,10 +4,10 @@ import os import zope.component -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -import letsencrypt.display.util as display_util +import certbot.display.util as display_util logger = logging.getLogger(__name__) @@ -50,7 +50,7 @@ def _vhost_menu(domain, vhosts): if free_chars < 2: logger.debug("Display size is too small for " - "letsencrypt_apache.display_ops._vhost_menu()") + "certbot_apache.display_ops._vhost_menu()") # This runs the edge off the screen, but it doesn't cause an "error" filename_size = 1 disp_name_size = 1 diff --git a/letsencrypt-apache/letsencrypt_apache/obj.py b/certbot-apache/certbot_apache/obj.py similarity index 98% rename from letsencrypt-apache/letsencrypt_apache/obj.py rename to certbot-apache/certbot_apache/obj.py index 80a49b6a6..b88b22428 100644 --- a/letsencrypt-apache/letsencrypt_apache/obj.py +++ b/certbot-apache/certbot_apache/obj.py @@ -1,7 +1,7 @@ """Module contains classes used by the Apache Configurator.""" import re -from letsencrypt.plugins import common +from certbot.plugins import common class Addr(common.Addr): @@ -208,7 +208,8 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods # If equal and set is not empty... assume same server if self.name is not None or self.aliases: return True - # If we're looking for a generic vhost, don't return one with a ServerName + # If we're looking for a generic vhost, + # don't return one with a ServerName elif self.name: return False diff --git a/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf b/certbot-apache/certbot_apache/options-ssl-apache.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf rename to certbot-apache/certbot_apache/options-ssl-apache.conf diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/certbot-apache/certbot_apache/parser.py similarity index 99% rename from letsencrypt-apache/letsencrypt_apache/parser.py rename to certbot-apache/certbot_apache/parser.py index f49ac0acc..321546eb3 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/certbot-apache/certbot_apache/parser.py @@ -6,9 +6,9 @@ import os import re import subprocess -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache import constants +from certbot_apache import constants logger = logging.getLogger(__name__) diff --git a/certbot-apache/certbot_apache/tests/__init__.py b/certbot-apache/certbot_apache/tests/__init__.py new file mode 100644 index 000000000..7e7d39fa4 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/__init__.py @@ -0,0 +1 @@ +"""Certbot Apache Tests""" diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt b/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt new file mode 100644 index 000000000..c3606fefe --- /dev/null +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt @@ -0,0 +1,6 @@ +Issues for which some kind of test case should be constructable, but we do not +currently have one: + +https://github.com/certbot/certbot/issues/1213 +https://github.com/certbot/certbot/issues/1602 + diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test similarity index 84% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test rename to certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test index 7b3f83d13..44268cb8f 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test @@ -5,9 +5,7 @@ export EA=/etc/apache2/ TESTDIR="`dirname $0`" -LEROOT="`realpath \"$TESTDIR/../../../../\"`" cd $TESTDIR/passing -LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}" function CleanupExit() { echo control c, exiting tests... @@ -21,13 +19,13 @@ function Setup() { if [ "$APPEND_APACHECONF" = "" ] ; then sudo cp "$f" "$EA"/sites-available/ sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" - sudo echo """ + echo " ServerName example.com DocumentRoot /tmp/ ErrorLog /tmp/error.log CustomLog /tmp/requests.log combined -""" >> $EA/sites-available/throwaway-example.conf +" | sudo tee $EA/sites-available/throwaway-example.conf >/dev/null else TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" sudo cp -a "$APPEND_APACHECONF" "$TMP" @@ -61,7 +59,7 @@ trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup - RESULT=`echo c | sudo "$LETSENCRYPT" -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo $(command -v certbot) -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then echo passed else diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093b.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093b.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/1626-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/1626-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/README.modules similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/README.modules diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/anarcat-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/anarcat-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-1755.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-1755.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-ssl.conf similarity index 99% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-ssl.conf index 466ac9ce3..31deb7647 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-ssl.conf @@ -39,7 +39,7 @@ # certificate chain for the server certificate. Alternatively # the referenced file can be the same as SSLCertificateFile # when the CA certificates are directly appended to the server - # certificate for convinience. + # certificate for convenience. #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt # Certificate Authority (CA): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143b.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143b.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143c.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143c.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143d.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143d.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/missing-quote-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/missing-quote-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/modmacro-1385.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/modmacro-1385.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/owncloud-1264.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/owncloud-1264.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/roundcube-1222.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/roundcube-1222.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/section-continuations-2525.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-continuations-2525.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/section-continuations-2525.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-continuations-2525.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/semacode-1598.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/semacode-1598.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py b/certbot-apache/certbot_apache/tests/augeas_configurator_test.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py rename to certbot-apache/certbot_apache/tests/augeas_configurator_test.py index bf95f72ce..c55f27ff0 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py +++ b/certbot-apache/certbot_apache/tests/augeas_configurator_test.py @@ -1,13 +1,13 @@ -"""Test for letsencrypt_apache.augeas_configurator.""" +"""Test for certbot_apache.augeas_configurator.""" import os import shutil import unittest import mock -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class AugeasConfiguratorTest(util.ApacheTest): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py b/certbot-apache/certbot_apache/tests/complex_parsing_test.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py rename to certbot-apache/certbot_apache/tests/complex_parsing_test.py index 1fc5281c1..079d7e95f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py +++ b/certbot-apache/certbot_apache/tests/complex_parsing_test.py @@ -1,11 +1,11 @@ -"""Tests for letsencrypt_apache.parser.""" +"""Tests for certbot_apache.parser.""" import os import shutil import unittest -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class ComplexParserTest(util.ParserTest): @@ -88,7 +88,7 @@ class ComplexParserTest(util.ParserTest): def verify_fnmatch(self, arg, hit=True): """Test if Include was correctly parsed.""" - from letsencrypt_apache import parser + from certbot_apache import parser self.parser.add_dir(parser.get_aug_path(self.parser.loc["default"]), "Include", [arg]) if hit: diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py similarity index 90% rename from letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py rename to certbot-apache/certbot_apache/tests/configurator_test.py index 927d918ae..f2f78c8f9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -1,5 +1,5 @@ # pylint: disable=too-many-public-methods -"""Test for letsencrypt_apache.configurator.""" +"""Test for certbot_apache.configurator.""" import os import shutil import socket @@ -9,15 +9,15 @@ import mock from acme import challenges -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.tests import acme_util +from certbot.tests import acme_util -from letsencrypt_apache import configurator -from letsencrypt_apache import obj +from certbot_apache import configurator +from certbot_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class MultipleVhostsTest(util.ApacheTest): @@ -38,7 +38,7 @@ class MultipleVhostsTest(util.ApacheTest): def mocked_deploy_cert(*args, **kwargs): """a helper to mock a deployed cert""" - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): + with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): config.real_deploy_cert(*args, **kwargs) self.config.deploy_cert = mocked_deploy_cert return self.config @@ -48,14 +48,14 @@ class MultipleVhostsTest(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_no_install(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( errors.NoInstallationError, self.config.prepare) - @mock.patch("letsencrypt_apache.parser.ApacheParser") - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.parser.ApacheParser") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_version(self, mock_exe_exists, _): mock_exe_exists.return_value = True self.config.version = None @@ -65,8 +65,8 @@ class MultipleVhostsTest(util.ApacheTest): self.assertRaises( errors.NotSupportedError, self.config.prepare) - @mock.patch("letsencrypt_apache.parser.ApacheParser") - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.parser.ApacheParser") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_old_aug(self, mock_exe_exists, _): mock_exe_exists.return_value = True self.config.config_test = mock.Mock() @@ -76,7 +76,7 @@ class MultipleVhostsTest(util.ApacheTest): errors.NotSupportedError, self.config.prepare) def test_add_parser_arguments(self): # pylint: disable=no-self-use - from letsencrypt_apache.configurator import ApacheConfigurator + from certbot_apache.configurator import ApacheConfigurator # Weak test.. ApacheConfigurator.add_parser_arguments(mock.MagicMock()) @@ -85,10 +85,10 @@ class MultipleVhostsTest(util.ApacheTest): mock_getutility.notification = mock.MagicMock(return_value=True) names = self.config.get_all_names() self.assertEqual(names, set( - ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"])) + ["certbot.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"])) @mock.patch("zope.component.getUtility") - @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") + @mock.patch("certbot_apache.configurator.socket.gethostbyaddr") def test_get_all_names_addrs(self, mock_gethost, mock_getutility): mock_gethost.side_effect = [("google.com", "", ""), socket.error] notification = mock.Mock() @@ -106,7 +106,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(len(names), 6) self.assertTrue("zombo.com" in names) self.assertTrue("google.com" in names) - self.assertTrue("letsencrypt.demo" in names) + self.assertTrue("certbot.demo" in names) def test_add_servernames_alias(self): self.config.parser.add_dir( @@ -139,25 +139,25 @@ class MultipleVhostsTest(util.ApacheTest): # Handle case of non-debian layout get_virtual_hosts with mock.patch( - "letsencrypt_apache.configurator.ApacheConfigurator.conf" + "certbot_apache.configurator.ApacheConfigurator.conf" ) as mock_conf: mock_conf.return_value = False vhs = self.config.get_virtual_hosts() self.assertEqual(len(vhs), 7) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_none_avail(self, mock_select): mock_select.return_value = None self.assertRaises( errors.PluginError, self.config.choose_vhost, "none.com") - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_ssl(self, mock_select): mock_select.return_value = self.vh_truth[1] self.assertEqual( self.vh_truth[1], self.config.choose_vhost("none.com")) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_non_ssl(self, mock_select): mock_select.return_value = self.vh_truth[0] chosen_vhost = self.config.choose_vhost("none.com") @@ -169,13 +169,13 @@ class MultipleVhostsTest(util.ApacheTest): self.assertFalse(self.vh_truth[0].ssl) self.assertTrue(chosen_vhost.ssl) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_with_temp(self, mock_select): mock_select.return_value = self.vh_truth[0] chosen_vhost = self.config.choose_vhost("none.com", temp=True) self.assertEqual(self.vh_truth[0], chosen_vhost) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_conflicting_non_ssl(self, mock_select): mock_select.return_value = self.vh_truth[3] conflicting_vhost = obj.VirtualHost( @@ -203,7 +203,7 @@ class MultipleVhostsTest(util.ApacheTest): def test_find_best_vhost(self): # pylint: disable=protected-access self.assertEqual( - self.vh_truth[3], self.config._find_best_vhost("letsencrypt.demo")) + self.vh_truth[3], self.config._find_best_vhost("certbot.demo")) self.assertEqual( self.vh_truth[0], self.config._find_best_vhost("encryption-example.demo")) @@ -224,7 +224,7 @@ class MultipleVhostsTest(util.ApacheTest): # Assume only the two default vhosts. self.config.vhosts = [ vh for vh in self.config.vhosts - if vh.name not in ["letsencrypt.demo", "encryption-example.demo"] + if vh.name not in ["certbot.demo", "encryption-example.demo"] and "*.blue.purple.com" not in vh.aliases ] @@ -254,9 +254,9 @@ class MultipleVhostsTest(util.ApacheTest): self.config.is_site_enabled, "irrelevant") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script): mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "") mock_popen().returncode = 0 @@ -273,7 +273,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertRaises( errors.NotSupportedError, self.config.enable_mod, "ssl") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.exe_exists") def test_enable_mod_no_disable(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( @@ -636,8 +636,8 @@ class MultipleVhostsTest(util.ApacheTest): self.config._add_name_vhost_if_necessary(self.vh_truth[0]) self.assertEqual(self.config.save.call_count, 2) - @mock.patch("letsencrypt_apache.configurator.tls_sni_01.ApacheTlsSni01.perform") - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.tls_sni_01.ApacheTlsSni01.perform") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_perform(self, mock_restart, mock_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -656,7 +656,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_cleanup(self, mock_restart): _, achall1, achall2 = self.get_achalls() @@ -669,7 +669,7 @@ class MultipleVhostsTest(util.ApacheTest): self.config.cleanup([achall2]) self.assertTrue(mock_restart.called) - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_cleanup_no_errors(self, mock_restart): _, achall1, achall2 = self.get_achalls() @@ -681,7 +681,7 @@ class MultipleVhostsTest(util.ApacheTest): self.config.cleanup([achall1, achall2]) self.assertTrue(mock_restart.called) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_get_version(self, mock_script): mock_script.return_value = ( "Server Version: Apache/2.4.2 (Debian)", "") @@ -703,21 +703,21 @@ class MultipleVhostsTest(util.ApacheTest): mock_script.side_effect = errors.SubprocessError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) - @mock.patch("letsencrypt_apache.configurator.le_util.run_script") + @mock.patch("certbot_apache.configurator.le_util.run_script") def test_restart(self, _): self.config.restart() - @mock.patch("letsencrypt_apache.configurator.le_util.run_script") + @mock.patch("certbot_apache.configurator.le_util.run_script") def test_restart_bad_process(self, mock_run_script): mock_run_script.side_effect = [None, errors.SubprocessError] self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test(self, _): self.config.config_test() - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test_bad_process(self, mock_run_script): mock_run_script.side_effect = errors.SubprocessError @@ -747,7 +747,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue(isinstance(self.config.get_chall_pref(""), list)) def test_install_ssl_options_conf(self): - from letsencrypt_apache.configurator import install_ssl_options_conf + from certbot_apache.configurator import install_ssl_options_conf path = os.path.join(self.work_dir, "test_it") install_ssl_options_conf(path) self.assertTrue(os.path.isfile(path)) @@ -756,7 +756,7 @@ class MultipleVhostsTest(util.ApacheTest): def test_supported_enhancements(self): self.assertTrue(isinstance(self.config.supported_enhancements(), list)) - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.exe_exists") def test_enhance_unknown_vhost(self, mock_exe): self.config.parser.modules.add("rewrite_module") mock_exe.return_value = True @@ -772,23 +772,23 @@ class MultipleVhostsTest(util.ApacheTest): def test_enhance_unknown_enhancement(self): self.assertRaises( errors.PluginError, - self.config.enhance, "letsencrypt.demo", "unknown_enhancement") + self.config.enhance, "certbot.demo", "unknown_enhancement") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_http_header_hsts(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") mock_exe.return_value = True - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "ensure-http-header", + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "ensure-http-header", "Strict-Transport-Security") self.assertTrue("headers_module" in self.config.parser.modules) - # Get the ssl vhost for letsencrypt.demo - ssl_vhost = self.config.assoc["letsencrypt.demo"] + # Get the ssl vhost for certbot.demo + ssl_vhost = self.config.assoc["certbot.demo"] # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -803,7 +803,7 @@ class MultipleVhostsTest(util.ApacheTest): # skip the enable mod self.config.parser.modules.add("headers_module") - # This will create an ssl vhost for letsencrypt.demo + # This will create an ssl vhost for certbot.demo self.config.enhance("encryption-example.demo", "ensure-http-header", "Strict-Transport-Security") @@ -812,21 +812,21 @@ class MultipleVhostsTest(util.ApacheTest): self.config.enhance, "encryption-example.demo", "ensure-http-header", "Strict-Transport-Security") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_http_header_uir(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") mock_exe.return_value = True - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "ensure-http-header", + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "ensure-http-header", "Upgrade-Insecure-Requests") self.assertTrue("headers_module" in self.config.parser.modules) - # Get the ssl vhost for letsencrypt.demo - ssl_vhost = self.config.assoc["letsencrypt.demo"] + # Get the ssl vhost for certbot.demo + ssl_vhost = self.config.assoc["certbot.demo"] # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -841,7 +841,7 @@ class MultipleVhostsTest(util.ApacheTest): # skip the enable mod self.config.parser.modules.add("headers_module") - # This will create an ssl vhost for letsencrypt.demo + # This will create an ssl vhost for certbot.demo self.config.enhance("encryption-example.demo", "ensure-http-header", "Upgrade-Insecure-Requests") @@ -850,15 +850,15 @@ class MultipleVhostsTest(util.ApacheTest): self.config.enhance, "encryption-example.demo", "ensure-http-header", "Upgrade-Insecure-Requests") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_redirect_well_formed_http(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True self.config.get_version = mock.Mock(return_value=(2, 2)) - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "redirect") + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "redirect") # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -894,8 +894,8 @@ class MultipleVhostsTest(util.ApacheTest): # pylint: disable=protected-access self.assertTrue(self.config._is_rewrite_engine_on(self.vh_truth[3])) - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_redirect_with_existing_rewrite(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True @@ -907,8 +907,8 @@ class MultipleVhostsTest(util.ApacheTest): "UnknownTarget"]) self.config.save() - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "redirect") + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "redirect") # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -981,7 +981,7 @@ class MultipleVhostsTest(util.ApacheTest): normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]" self.assertFalse(self.config._sift_line(normal_target)) - @mock.patch("letsencrypt_apache.configurator.zope.component.getUtility") + @mock.patch("certbot_apache.configurator.zope.component.getUtility") def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility): self.config.parser.modules.add("rewrite_module") @@ -1024,7 +1024,7 @@ class MultipleVhostsTest(util.ApacheTest): challenges.TLSSNI01( token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"), "pending"), - domain="letsencrypt.demo", account_key=account_key) + domain="certbot.demo", account_key=account_key) return account_key, achall1, achall2 diff --git a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py b/certbot-apache/certbot_apache/tests/constants_test.py similarity index 75% rename from letsencrypt-apache/letsencrypt_apache/tests/constants_test.py rename to certbot-apache/certbot_apache/tests/constants_test.py index 289b61bb1..d970c96be 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py +++ b/certbot-apache/certbot_apache/tests/constants_test.py @@ -1,26 +1,26 @@ -"""Test for letsencrypt_apache.configurator.""" +"""Test for certbot_apache.configurator.""" import mock import unittest -from letsencrypt_apache import constants +from certbot_apache import constants class ConstantsTest(unittest.TestCase): - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_debian_value(self, os_info): os_info.return_value = ('Debian', '', '') self.assertEqual(constants.os_constant("vhost_root"), "/etc/apache2/sites-available") - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_centos_value(self, os_info): os_info.return_value = ('CentOS Linux', '', '') self.assertEqual(constants.os_constant("vhost_root"), "/etc/httpd/conf.d") - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_default_value(self, os_info): os_info.return_value = ('Nonexistent Linux', '', '') self.assertEqual(constants.os_constant("vhost_root"), diff --git a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py b/certbot-apache/certbot_apache/tests/display_ops_test.py similarity index 71% rename from letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py rename to certbot-apache/certbot_apache/tests/display_ops_test.py index 124ba2f17..fd1e52fde 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py +++ b/certbot-apache/certbot_apache/tests/display_ops_test.py @@ -1,20 +1,20 @@ -"""Test letsencrypt_apache.display_ops.""" +"""Test certbot_apache.display_ops.""" import sys import unittest import mock import zope.component -from letsencrypt.display import util as display_util -from letsencrypt import errors +from certbot.display import util as display_util +from certbot import errors -from letsencrypt_apache import obj +from certbot_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class SelectVhostTest(unittest.TestCase): - """Tests for letsencrypt_apache.display_ops.select_vhost.""" + """Tests for certbot_apache.display_ops.select_vhost.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -24,15 +24,15 @@ class SelectVhostTest(unittest.TestCase): @classmethod def _call(cls, vhosts): - from letsencrypt_apache.display_ops import select_vhost + from certbot_apache.display_ops import select_vhost return select_vhost("example.com", vhosts) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_successful_choice(self, mock_util): mock_util().menu.return_value = (display_util.OK, 3) self.assertEqual(self.vhosts[3], self._call(self.vhosts)) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_noninteractive(self, mock_util): mock_util().menu.side_effect = errors.MissingCommandlineFlag("no vhost default") try: @@ -40,7 +40,7 @@ class SelectVhostTest(unittest.TestCase): except errors.MissingCommandlineFlag as e: self.assertTrue("VirtualHost directives" in e.message) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_more_info_cancel(self, mock_util): mock_util().menu.side_effect = [ (display_util.HELP, 1), @@ -54,9 +54,9 @@ class SelectVhostTest(unittest.TestCase): def test_no_vhosts(self): self.assertEqual(self._call([]), None) - @mock.patch("letsencrypt_apache.display_ops.display_util") - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") - @mock.patch("letsencrypt_apache.display_ops.logger") + @mock.patch("certbot_apache.display_ops.display_util") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.logger") def test_small_display(self, mock_logger, mock_util, mock_display_util): mock_display_util.WIDTH = 20 mock_util().menu.return_value = (display_util.OK, 0) @@ -64,7 +64,7 @@ class SelectVhostTest(unittest.TestCase): self.assertEqual(mock_logger.debug.call_count, 1) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_multiple_names(self, mock_util): mock_util().menu.return_value = (display_util.OK, 5) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/obj_test.py b/certbot-apache/certbot_apache/tests/obj_test.py similarity index 92% rename from letsencrypt-apache/letsencrypt_apache/tests/obj_test.py rename to certbot-apache/certbot_apache/tests/obj_test.py index a469702f1..4c3d331be 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/obj_test.py +++ b/certbot-apache/certbot_apache/tests/obj_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_apache.obj.""" +"""Tests for certbot_apache.obj.""" import unittest @@ -6,8 +6,8 @@ class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): - from letsencrypt_apache.obj import Addr - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import Addr + from certbot_apache.obj import VirtualHost self.addr1 = Addr.fromstring("127.0.0.1") self.addr2 = Addr.fromstring("127.0.0.1:443") @@ -33,8 +33,8 @@ class VirtualHostTest(unittest.TestCase): self.assertFalse(self.vhost1 != self.vhost1b) def test_conflicts(self): - from letsencrypt_apache.obj import Addr - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import Addr + from certbot_apache.obj import VirtualHost complex_vh = VirtualHost( "fp", "vhp", @@ -51,7 +51,7 @@ class VirtualHostTest(unittest.TestCase): self.addr_default])) def test_same_server(self): - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import VirtualHost no_name1 = VirtualHost( "fp", "vhp", set([self.addr1]), False, False, None) no_name2 = VirtualHost( @@ -74,7 +74,7 @@ class VirtualHostTest(unittest.TestCase): class AddrTest(unittest.TestCase): """Test obj.Addr.""" def setUp(self): - from letsencrypt_apache.obj import Addr + from certbot_apache.obj import Addr self.addr = Addr.fromstring("*:443") self.addr1 = Addr.fromstring("127.0.0.1") @@ -89,7 +89,7 @@ class AddrTest(unittest.TestCase): self.assertTrue(self.addr2.is_wildcard()) def test_get_sni_addr(self): - from letsencrypt_apache.obj import Addr + from certbot_apache.obj import Addr self.assertEqual( self.addr.get_sni_addr("443"), Addr.fromstring("*:443")) self.assertEqual( diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/certbot-apache/certbot_apache/tests/parser_test.py similarity index 83% rename from letsencrypt-apache/letsencrypt_apache/tests/parser_test.py rename to certbot-apache/certbot_apache/tests/parser_test.py index f4d4660c9..759ae1265 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/certbot-apache/certbot_apache/tests/parser_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_apache.parser.""" +"""Tests for certbot_apache.parser.""" import os import shutil import unittest @@ -6,9 +6,9 @@ import unittest import augeas import mock -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class BasicParserTest(util.ParserTest): @@ -31,12 +31,12 @@ class BasicParserTest(util.ParserTest): def test_parse_file(self): """Test parse_file. - letsencrypt.conf is chosen as the test file as it will not be + certbot.conf is chosen as the test file as it will not be included during the normal course of execution. """ file_path = os.path.join( - self.config_path, "not-parsed-by-default", "letsencrypt.conf") + self.config_path, "not-parsed-by-default", "certbot.conf") self.parser._parse_file(file_path) # pylint: disable=protected-access @@ -72,7 +72,7 @@ class BasicParserTest(util.ParserTest): Path must be valid before attempting to add to augeas """ - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path # This makes sure that find_dir will work self.parser.modules.add("mod_ssl.c") @@ -86,7 +86,7 @@ class BasicParserTest(util.ParserTest): self.assertTrue("IfModule" in matches[0]) def test_add_dir_to_ifmodssl_multiple(self): - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path # This makes sure that find_dir will work self.parser.modules.add("mod_ssl.c") @@ -100,11 +100,11 @@ class BasicParserTest(util.ParserTest): self.assertTrue("IfModule" in matches[0]) def test_get_aug_path(self): - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache")) def test_set_locations(self): - with mock.patch("letsencrypt_apache.parser.os.path") as mock_path: + with mock.patch("certbot_apache.parser.os.path") as mock_path: mock_path.isfile.side_effect = [False, False] @@ -114,7 +114,7 @@ class BasicParserTest(util.ParserTest): self.assertEqual(results["default"], results["listen"]) self.assertEqual(results["default"], results["name"]) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_variables(self, mock_cfg): mock_cfg.return_value = ( 'ServerRoot: "/etc/apache2"\n' @@ -139,7 +139,7 @@ class BasicParserTest(util.ParserTest): self.parser.update_runtime_variables() self.assertEqual(self.parser.variables, expected_vars) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): mock_cfg.return_value = "Define: TLS=443=24" self.parser.update_runtime_variables() @@ -148,8 +148,8 @@ class BasicParserTest(util.ParserTest): self.assertRaises( errors.PluginError, self.parser.update_runtime_variables) - @mock.patch("letsencrypt_apache.constants.os_constant") - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot_apache.constants.os_constant") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const): mock_popen.side_effect = OSError mock_const.return_value = "nonexistent" @@ -157,7 +157,7 @@ class BasicParserTest(util.ParserTest): errors.MisconfigurationError, self.parser.update_runtime_variables) - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_exit(self, mock_popen): mock_popen().communicate.return_value = ("", "") mock_popen.returncode = -1 @@ -177,9 +177,9 @@ class ParserInitTest(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_unparsable(self, mock_cfg): - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser mock_cfg.return_value = ('Define: TEST') self.assertRaises( errors.PluginError, @@ -187,9 +187,9 @@ class ParserInitTest(util.ApacheTest): "/dummy/vhostpath", version=(2, 2, 22)) def test_root_normalized(self): - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): path = os.path.join( self.temp_dir, @@ -201,8 +201,8 @@ class ParserInitTest(util.ApacheTest): self.assertEqual(parser.root, self.config_path) def test_root_absolute(self): - from letsencrypt_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + from certbot_apache.parser import ApacheParser + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( self.aug, os.path.relpath(self.config_path), @@ -211,8 +211,8 @@ class ParserInitTest(util.ApacheTest): self.assertEqual(parser.root, self.config_path) def test_root_no_trailing_slash(self): - from letsencrypt_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + from certbot_apache.parser import ApacheParser + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( self.aug, self.config_path + os.path.sep, diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_fnmatch.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_fnmatch.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_fnmatch.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_fnmatch.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_variables.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_variables.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf similarity index 76% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf index 8da335d35..d81fe132d 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf @@ -1,5 +1,5 @@ - # How well does Let's Encrypt work without a ServerName/Alias? + # How well does Certbot work without a ServerName/Alias? ServerAdmin webmaster@localhost DocumentRoot /var/www/html diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf similarity index 88% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf index 2fbfc02a8..e659d4b07 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf @@ -16,8 +16,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem SSLOptions +StdEnvVars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/sites similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/sites diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load diff --git a/letsencrypt-apache/docs/_static/.gitignore b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore similarity index 100% rename from letsencrypt-apache/docs/_static/.gitignore rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf similarity index 91% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf index e38fc9f9b..b3147a523 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf @@ -1,8 +1,8 @@ -ServerName letsencrypt.demo +ServerName certbot.demo ServerAdmin webmaster@localhost -DocumentRoot /var/www-letsencrypt-reworld/static/ +DocumentRoot /var/www-certbot-reworld/static/ Options FollowSymLinks AllowOverride None diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf similarity index 88% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf index 5a50c536e..849b42e9f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf @@ -12,8 +12,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf similarity index 89% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf index f1061c928..a3025ae8a 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf @@ -16,8 +16,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf new file mode 120000 index 000000000..4d08c763f --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf @@ -0,0 +1 @@ +../sites-available/certbot.conf \ No newline at end of file diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites similarity index 56% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites index 3e73390fd..06bf6a2ae 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites @@ -1,2 +1,2 @@ -sites-available/letsencrypt.conf, letsencrypt.demo +sites-available/certbot.conf, certbot.demo sites-available/encryption-example.conf, encryption-example.demo diff --git a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py similarity index 91% rename from letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py rename to certbot-apache/certbot_apache/tests/tls_sni_01_test.py index 9681bf9fc..17ef92004 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py +++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py @@ -1,13 +1,13 @@ -"""Test for letsencrypt_apache.tls_sni_01.""" +"""Test for certbot_apache.tls_sni_01.""" import unittest import shutil import mock -from letsencrypt.plugins import common_test +from certbot.plugins import common_test -from letsencrypt_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache import obj +from certbot_apache.tests import util from six.moves import xrange # pylint: disable=redefined-builtin, import-error @@ -25,7 +25,7 @@ class TlsSniPerformTest(util.ApacheTest): self.config_path, self.vhost_path, self.config_dir, self.work_dir) config.config.tls_sni_01_port = 443 - from letsencrypt_apache import tls_sni_01 + from certbot_apache import tls_sni_01 self.sni = tls_sni_01.ApacheTlsSni01(config) def tearDown(self): @@ -37,8 +37,8 @@ class TlsSniPerformTest(util.ApacheTest): resp = self.sni.perform() self.assertEqual(len(resp), 0) - @mock.patch("letsencrypt.le_util.exe_exists") - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") def test_perform1(self, _, mock_exists): mock_register = mock.Mock() self.sni.configurator.reverter.register_undo_command = mock_register @@ -80,7 +80,7 @@ class TlsSniPerformTest(util.ApacheTest): # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): + with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/certbot-apache/certbot_apache/tests/util.py similarity index 87% rename from letsencrypt-apache/letsencrypt_apache/tests/util.py rename to certbot-apache/certbot_apache/tests/util.py index 2fbfd70c6..9fb5dcdfa 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/certbot-apache/certbot_apache/tests/util.py @@ -1,4 +1,4 @@ -"""Common utilities for letsencrypt_apache.""" +"""Common utilities for certbot_apache.""" import os import sys import unittest @@ -9,15 +9,15 @@ import zope.component from acme import jose -from letsencrypt.display import util as display_util +from certbot.display import util as display_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt.tests import test_util +from certbot.tests import test_util -from letsencrypt_apache import configurator -from letsencrypt_apache import constants -from letsencrypt_apache import obj +from certbot_apache import configurator +from certbot_apache import constants +from certbot_apache import obj class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -30,7 +30,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( test_dir=test_dir, - pkg="letsencrypt_apache.tests") + pkg="certbot_apache.tests") self.ssl_options = common.setup_ssl_options( self.config_dir, constants.os_constant("MOD_SSL_CONF_SRC"), @@ -66,10 +66,10 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser self.aug = augeas.Augeas( flags=augeas.Augeas.NONE | augeas.Augeas.NO_MODL_AUTOLOAD) - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): self.parser = ApacheParser( self.aug, self.config_path, self.vhost_path) @@ -95,11 +95,11 @@ def get_apache_configurator( in_progress_dir=os.path.join(backups, "IN_PROGRESS"), work_dir=work_dir) - with mock.patch("letsencrypt_apache.configurator.le_util.run_script"): - with mock.patch("letsencrypt_apache.configurator.le_util." + with mock.patch("certbot_apache.configurator.le_util.run_script"): + with mock.patch("certbot_apache.configurator.le_util." "exe_exists") as mock_exe_exists: mock_exe_exists.return_value = True - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): config = configurator.ApacheConfigurator( config=mock_le_config, @@ -137,10 +137,10 @@ def get_vh_truth(temp_dir, config_name): obj.Addr.fromstring("[::]:80")]), False, True, "ip-172-30-0-17"), obj.VirtualHost( - os.path.join(prefix, "letsencrypt.conf"), - os.path.join(aug_pre, "letsencrypt.conf/VirtualHost"), + os.path.join(prefix, "certbot.conf"), + os.path.join(aug_pre, "certbot.conf/VirtualHost"), set([obj.Addr.fromstring("*:80")]), False, True, - "letsencrypt.demo"), + "certbot.demo"), obj.VirtualHost( os.path.join(prefix, "mod_macro-example.conf"), os.path.join(aug_pre, diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/tls_sni_01.py rename to certbot-apache/certbot_apache/tls_sni_01.py index 3af61a7e7..1236c2eb9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/certbot-apache/certbot_apache/tls_sni_01.py @@ -3,10 +3,10 @@ import os import logging -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_apache import obj -from letsencrypt_apache import parser +from certbot_apache import obj +from certbot_apache import parser logger = logging.getLogger(__name__) @@ -145,7 +145,7 @@ class ApacheTlsSni01(common.TLSSNI01): parser.case_i("Include"), self.challenge_conf)) == 0: # print "Including challenge virtual host(s)" logger.debug("Adding Include %s to %s", - self.challenge_conf, parser.get_aug_path(main_config)) + self.challenge_conf, parser.get_aug_path(main_config)) self.configurator.parser.add_dir( parser.get_aug_path(main_config), "Include", self.challenge_conf) diff --git a/letsencrypt-apache/docs/.gitignore b/certbot-apache/docs/.gitignore similarity index 100% rename from letsencrypt-apache/docs/.gitignore rename to certbot-apache/docs/.gitignore diff --git a/letsencrypt-nginx/docs/Makefile b/certbot-apache/docs/Makefile similarity index 96% rename from letsencrypt-nginx/docs/Makefile rename to certbot-apache/docs/Makefile index 3a3828235..0e611ecec 100644 --- a/letsencrypt-nginx/docs/Makefile +++ b/certbot-apache/docs/Makefile @@ -87,9 +87,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-nginx.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-apache.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-nginx.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-apache.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-nginx" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-nginx" + @echo "# mkdir -p $$HOME/.local/share/devhelp/certbot-apache" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-apache" @echo "# devhelp" epub: diff --git a/letsencrypt-apache/docs/_templates/.gitignore b/certbot-apache/docs/_static/.gitignore similarity index 100% rename from letsencrypt-apache/docs/_templates/.gitignore rename to certbot-apache/docs/_static/.gitignore diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore b/certbot-apache/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore rename to certbot-apache/docs/_templates/.gitignore diff --git a/letsencrypt-apache/docs/api.rst b/certbot-apache/docs/api.rst similarity index 100% rename from letsencrypt-apache/docs/api.rst rename to certbot-apache/docs/api.rst diff --git a/certbot-apache/docs/api/augeas_configurator.rst b/certbot-apache/docs/api/augeas_configurator.rst new file mode 100644 index 000000000..b47ffbc6b --- /dev/null +++ b/certbot-apache/docs/api/augeas_configurator.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.augeas_configurator` +--------------------------------------------- + +.. automodule:: certbot_apache.augeas_configurator + :members: diff --git a/certbot-apache/docs/api/configurator.rst b/certbot-apache/docs/api/configurator.rst new file mode 100644 index 000000000..8ec266d1a --- /dev/null +++ b/certbot-apache/docs/api/configurator.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.configurator` +-------------------------------------- + +.. automodule:: certbot_apache.configurator + :members: diff --git a/certbot-apache/docs/api/display_ops.rst b/certbot-apache/docs/api/display_ops.rst new file mode 100644 index 000000000..26d3ed3dc --- /dev/null +++ b/certbot-apache/docs/api/display_ops.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.display_ops` +------------------------------------- + +.. automodule:: certbot_apache.display_ops + :members: diff --git a/certbot-apache/docs/api/obj.rst b/certbot-apache/docs/api/obj.rst new file mode 100644 index 000000000..82e58df3f --- /dev/null +++ b/certbot-apache/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.obj` +----------------------------- + +.. automodule:: certbot_apache.obj + :members: diff --git a/certbot-apache/docs/api/parser.rst b/certbot-apache/docs/api/parser.rst new file mode 100644 index 000000000..3427735be --- /dev/null +++ b/certbot-apache/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.parser` +-------------------------------- + +.. automodule:: certbot_apache.parser + :members: diff --git a/certbot-apache/docs/api/tls_sni_01.rst b/certbot-apache/docs/api/tls_sni_01.rst new file mode 100644 index 000000000..3ecd0a365 --- /dev/null +++ b/certbot-apache/docs/api/tls_sni_01.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.tls_sni_01` +------------------------------------ + +.. automodule:: certbot_apache.tls_sni_01 + :members: diff --git a/letsencrypt-apache/docs/conf.py b/certbot-apache/docs/conf.py similarity index 94% rename from letsencrypt-apache/docs/conf.py rename to certbot-apache/docs/conf.py index aa58038cd..2f996c7f4 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/certbot-apache/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-apache documentation build configuration file, created by +# certbot-apache documentation build configuration file, created by # sphinx-quickstart on Sun Oct 18 13:39:26 2015. # # This file is execfile()d with the current directory set to its @@ -65,9 +65,9 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-apache' +project = u'certbot-apache' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -227,7 +227,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-apachedoc' +htmlhelp_basename = 'certbot-apachedoc' # -- Options for LaTeX output --------------------------------------------- @@ -249,8 +249,8 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letsencrypt-apache.tex', u'letsencrypt-apache Documentation', - u'Let\'s Encrypt Project', 'manual'), + (master_doc, 'certbot-apache.tex', u'certbot-apache Documentation', + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -279,7 +279,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'letsencrypt-apache', u'letsencrypt-apache Documentation', + (master_doc, 'certbot-apache', u'certbot-apache Documentation', [author], 1) ] @@ -293,8 +293,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'letsencrypt-apache', u'letsencrypt-apache Documentation', - author, 'letsencrypt-apache', 'One line description of project.', + (master_doc, 'certbot-apache', u'certbot-apache Documentation', + author, 'certbot-apache', 'One line description of project.', 'Miscellaneous'), ] @@ -314,5 +314,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-apache/docs/index.rst b/certbot-apache/docs/index.rst similarity index 74% rename from letsencrypt-apache/docs/index.rst rename to certbot-apache/docs/index.rst index f968ccbef..bfe4d245c 100644 --- a/letsencrypt-apache/docs/index.rst +++ b/certbot-apache/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-apache documentation master file, created by +.. certbot-apache documentation master file, created by sphinx-quickstart on Sun Oct 18 13:39:26 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to letsencrypt-apache's documentation! +Welcome to certbot-apache's documentation! ============================================== Contents: @@ -18,7 +18,7 @@ Contents: api -.. automodule:: letsencrypt_apache +.. automodule:: certbot_apache :members: diff --git a/letsencrypt-nginx/docs/make.bat b/certbot-apache/docs/make.bat similarity index 97% rename from letsencrypt-nginx/docs/make.bat rename to certbot-apache/docs/make.bat index eb19a3fb5..3a7818940 100644 --- a/letsencrypt-nginx/docs/make.bat +++ b/certbot-apache/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-nginx.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-apache.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-nginx.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-apache.ghc goto end ) diff --git a/letsencrypt-nginx/readthedocs.org.requirements.txt b/certbot-apache/readthedocs.org.requirements.txt similarity index 94% rename from letsencrypt-nginx/readthedocs.org.requirements.txt rename to certbot-apache/readthedocs.org.requirements.txt index 3b55df408..fe30ab1dc 100644 --- a/letsencrypt-nginx/readthedocs.org.requirements.txt +++ b/certbot-apache/readthedocs.org.requirements.txt @@ -9,4 +9,4 @@ -e acme -e . --e letsencrypt-nginx[docs] +-e certbot-apache[docs] diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py new file mode 100644 index 000000000..7358c7041 --- /dev/null +++ b/certbot-apache/setup.py @@ -0,0 +1,69 @@ +import sys + +from setuptools import setup +from setuptools import find_packages + + +version = '0.6.0.dev0' + +# Please update tox.ini when modifying dependency version requirements +install_requires = [ + 'acme=={0}'.format(version), + 'certbot=={0}'.format(version), + 'python-augeas', + # For pkg_resources. >=1.0 so pip resolves it to a version cryptography + # will tolerate; see #2599: + 'setuptools>=1.0', + 'zope.component', + 'zope.interface', +] + +if sys.version_info < (2, 7): + install_requires.append('mock<1.1.0') +else: + install_requires.append('mock') + +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + +setup( + name='certbot-apache', + version=version, + description="Apache plugin for Certbot", + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, + entry_points={ + 'certbot.plugins': [ + 'apache = certbot_apache.configurator:ApacheConfigurator', + ], + }, + test_suite='certbot_apache', +) diff --git a/certbot-compatibility-test/LICENSE.txt b/certbot-compatibility-test/LICENSE.txt new file mode 100644 index 000000000..981c46c9f --- /dev/null +++ b/certbot-compatibility-test/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/certbot-compatibility-test/MANIFEST.in b/certbot-compatibility-test/MANIFEST.in new file mode 100644 index 000000000..11762538a --- /dev/null +++ b/certbot-compatibility-test/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +include certbot_compatibility_test/configurators/apache/a2enmod.sh +include certbot_compatibility_test/configurators/apache/a2dismod.sh +include certbot_compatibility_test/configurators/apache/Dockerfile +recursive-include certbot_compatibility_test/testdata * diff --git a/certbot-compatibility-test/README.rst b/certbot-compatibility-test/README.rst new file mode 100644 index 000000000..9333b5680 --- /dev/null +++ b/certbot-compatibility-test/README.rst @@ -0,0 +1 @@ +Compatibility tests for Certbot diff --git a/certbot-compatibility-test/certbot_compatibility_test/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/__init__.py new file mode 100644 index 000000000..5ee547703 --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/__init__.py @@ -0,0 +1 @@ +"""Certbot compatibility test""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py new file mode 100644 index 000000000..e553ff438 --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py @@ -0,0 +1 @@ +"""Certbot compatibility test configurators""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile new file mode 100644 index 000000000..ea9bb857f --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile @@ -0,0 +1,20 @@ +FROM httpd +MAINTAINER Brad Warren + +RUN mkdir /var/run/apache2 + +ENV APACHE_RUN_USER=daemon \ + APACHE_RUN_GROUP=daemon \ + APACHE_PID_FILE=/usr/local/apache2/logs/httpd.pid \ + APACHE_RUN_DIR=/var/run/apache2 \ + APACHE_LOCK_DIR=/var/lock \ + APACHE_LOG_DIR=/usr/local/apache2/logs + +COPY certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh /usr/local/bin/ +COPY certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh /usr/local/bin/ +COPY certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem /usr/local/apache2/conf/ +COPY certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem /usr/local/apache2/conf/ + +# Note: this only exposes the port to other docker containers. You +# still have to bind to 443@host at runtime. +EXPOSE 443 diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py new file mode 100644 index 000000000..d559d0645 --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py @@ -0,0 +1 @@ +"""Certbot compatibility test Apache configurators""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py similarity index 93% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py index a68f53689..927c329ef 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py @@ -2,9 +2,9 @@ import zope.interface -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import interfaces -from letsencrypt_compatibility_test.configurators.apache import common as apache_common +from certbot_compatibility_test import errors +from certbot_compatibility_test import interfaces +from certbot_compatibility_test.configurators.apache import common as apache_common # The docker image doesn't actually have the watchdog module, but unless the diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py similarity index 93% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py index d383963a3..f57e0512d 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py @@ -6,13 +6,13 @@ import subprocess import mock import zope.interface -from letsencrypt import configuration -from letsencrypt import errors as le_errors -from letsencrypt_apache import configurator -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import interfaces -from letsencrypt_compatibility_test import util -from letsencrypt_compatibility_test.configurators import common as configurators_common +from certbot import configuration +from certbot import errors as le_errors +from certbot_apache import configurator +from certbot_compatibility_test import errors +from certbot_compatibility_test import interfaces +from certbot_compatibility_test import util +from certbot_compatibility_test.configurators import common as configurators_common APACHE_VERSION_REGEX = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) @@ -41,20 +41,20 @@ class Proxy(configurators_common.Proxy): mock_subprocess.Popen = self.popen mock.patch( - "letsencrypt_apache.configurator.subprocess", + "certbot_apache.configurator.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt_apache.parser.subprocess", + "certbot_apache.parser.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt.le_util.subprocess", + "certbot.le_util.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt_apache.configurator.le_util.exe_exists", + "certbot_apache.configurator.le_util.exe_exists", _is_apache_command).start() patch = mock.patch( - "letsencrypt_apache.configurator.display_ops.select_vhost") + "certbot_apache.configurator.display_ops.select_vhost") mock_display = patch.start() mock_display.side_effect = le_errors.PluginError( "Unable to determine vhost") diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py similarity index 97% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/common.py index 7c5e5dfcb..4657883a3 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py @@ -6,9 +6,9 @@ import tempfile import docker -from letsencrypt import constants -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import util +from certbot import constants +from certbot_compatibility_test import errors +from certbot_compatibility_test import util logger = logging.getLogger(__name__) diff --git a/certbot-compatibility-test/certbot_compatibility_test/errors.py b/certbot-compatibility-test/certbot_compatibility_test/errors.py new file mode 100644 index 000000000..e6a235e70 --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/errors.py @@ -0,0 +1,5 @@ +"""Certbot compatibility test errors""" + + +class Error(Exception): + """Generic Certbot compatibility test error""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py similarity index 74% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py rename to certbot-compatibility-test/certbot_compatibility_test/interfaces.py index fcf7a504f..cd367d9af 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py +++ b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py @@ -1,13 +1,13 @@ -"""Let's Encrypt compatibility test interfaces""" +"""Certbot compatibility test interfaces""" import zope.interface -import letsencrypt.interfaces +import certbot.interfaces # pylint: disable=no-self-argument,no-method-argument class IPluginProxy(zope.interface.Interface): - """Wraps a Let's Encrypt plugin""" + """Wraps a Certbot plugin""" http_port = zope.interface.Attribute( "The port to connect to on localhost for HTTP traffic") @@ -37,16 +37,16 @@ class IPluginProxy(zope.interface.Interface): """Returns the domain names that can be used in testing""" -class IAuthenticatorProxy(IPluginProxy, letsencrypt.interfaces.IAuthenticator): - """Wraps a Let's Encrypt authenticator""" +class IAuthenticatorProxy(IPluginProxy, certbot.interfaces.IAuthenticator): + """Wraps a Certbot authenticator""" -class IInstallerProxy(IPluginProxy, letsencrypt.interfaces.IInstaller): - """Wraps a Let's Encrypt installer""" +class IInstallerProxy(IPluginProxy, certbot.interfaces.IInstaller): + """Wraps a Certbot installer""" def get_all_names_answer(): """Returns all names that should be found by the installer""" class IConfiguratorProxy(IAuthenticatorProxy, IInstallerProxy): - """Wraps a Let's Encrypt configurator""" + """Wraps a Certbot configurator""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py similarity index 95% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py rename to certbot-compatibility-test/certbot_compatibility_test/test_driver.py index ee679bdb7..6823dfdab 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py +++ b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py @@ -1,4 +1,4 @@ -"""Tests Let's Encrypt plugins against different server configurations.""" +"""Tests Certbot plugins against different server configurations.""" import argparse import filecmp import functools @@ -13,19 +13,19 @@ import OpenSSL from acme import challenges from acme import crypto_util from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors as le_errors -from letsencrypt.tests import acme_util +from certbot import achallenges +from certbot import errors as le_errors +from certbot.tests import acme_util -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import util -from letsencrypt_compatibility_test import validator +from certbot_compatibility_test import errors +from certbot_compatibility_test import util +from certbot_compatibility_test import validator -from letsencrypt_compatibility_test.configurators.apache import apache24 +from certbot_compatibility_test.configurators.apache import apache24 DESCRIPTION = """ -Tests Let's Encrypt plugins against different server configuratons. It is +Tests Certbot plugins against different server configuratons. It is assumed that Docker is already installed. If no test types is specified, all tests that the plugin supports are performed. diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/configs.tar.gz b/certbot-compatibility-test/certbot_compatibility_test/testdata/configs.tar.gz similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/configs.tar.gz rename to certbot-compatibility-test/certbot_compatibility_test/testdata/configs.tar.gz diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py b/certbot-compatibility-test/certbot_compatibility_test/util.py similarity index 90% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py rename to certbot-compatibility-test/certbot_compatibility_test/util.py index b635ee539..cbce4fb56 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py +++ b/certbot-compatibility-test/certbot_compatibility_test/util.py @@ -1,4 +1,4 @@ -"""Utility functions for Let"s Encrypt plugin tests.""" +"""Utility functions for Certbot plugin tests.""" import argparse import copy import contextlib @@ -10,9 +10,9 @@ import tarfile from acme import jose from acme import test_util -from letsencrypt import constants +from certbot import constants -from letsencrypt_compatibility_test import errors +from certbot_compatibility_test import errors _KEY_BASE = "rsa1024_key.pem" @@ -26,7 +26,7 @@ def create_le_config(parent_dir): """Sets up LE dirs in parent_dir and returns the config dict""" config = copy.deepcopy(constants.CLI_DEFAULTS) - le_dir = os.path.join(parent_dir, "letsencrypt") + le_dir = os.path.join(parent_dir, "certbot") config["config_dir"] = os.path.join(le_dir, "config") config["work_dir"] = os.path.join(le_dir, "work") config["logs_dir"] = os.path.join(le_dir, "logs_dir") diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py b/certbot-compatibility-test/certbot_compatibility_test/validator.py similarity index 98% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py rename to certbot-compatibility-test/certbot_compatibility_test/validator.py index 90ce108c0..e82b2c049 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py +++ b/certbot-compatibility-test/certbot_compatibility_test/validator.py @@ -6,7 +6,7 @@ import zope.interface from acme import crypto_util from acme import errors as acme_errors -from letsencrypt import interfaces +from certbot import interfaces logger = logging.getLogger(__name__) diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py b/certbot-compatibility-test/certbot_compatibility_test/validator_test.py similarity index 78% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py rename to certbot-compatibility-test/certbot_compatibility_test/validator_test.py index 3a3bbc4b2..d0552a756 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py +++ b/certbot-compatibility-test/certbot_compatibility_test/validator_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_compatibility_test.validator.""" +"""Tests for certbot_compatibility_test.validator.""" import requests import unittest @@ -6,7 +6,7 @@ import mock import OpenSSL from acme import errors as acme_errors -from letsencrypt_compatibility_test import validator +from certbot_compatibility_test import validator class ValidatorTest(unittest.TestCase): @@ -14,7 +14,7 @@ class ValidatorTest(unittest.TestCase): self.validator = validator.Validator() @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_success(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.return_value = cert @@ -22,7 +22,7 @@ class ValidatorTest(unittest.TestCase): cert, "test.com", "127.0.0.1")) @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_error(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.side_effect = [acme_errors.Error] @@ -30,7 +30,7 @@ class ValidatorTest(unittest.TestCase): cert, "test.com", "127.0.0.1")) @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_failure(self, mock_probe_sni): cert = OpenSSL.crypto.X509() cert.set_serial_number(1337) @@ -38,67 +38,67 @@ class ValidatorTest(unittest.TestCase): self.assertFalse(self.validator.certificate( cert, "test.com", "127.0.0.1")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_succesful_redirect(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_with_headers(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect( "test.com", headers={"Host": "test.com"})) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_missing_location(self, mock_get_request): mock_get_request.return_value = create_response(301) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_wrong_status_code(self, mock_get_request): mock_get_request.return_value = create_response( 201, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_wrong_redirect_code(self, mock_get_request): mock_get_request.return_value = create_response( 303, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_empty(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": ""}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_malformed(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "sdfal"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_bad_max_age(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=not-an-int"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_expire(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=3600"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=31536000"}) self.assertTrue(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_include_subdomains(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": diff --git a/letsencrypt-compatibility-test/docs/.gitignore b/certbot-compatibility-test/docs/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/.gitignore rename to certbot-compatibility-test/docs/.gitignore diff --git a/letsencrypt-compatibility-test/docs/Makefile b/certbot-compatibility-test/docs/Makefile similarity index 96% rename from letsencrypt-compatibility-test/docs/Makefile rename to certbot-compatibility-test/docs/Makefile index 90582a59b..0c9cf40aa 100644 --- a/letsencrypt-compatibility-test/docs/Makefile +++ b/certbot-compatibility-test/docs/Makefile @@ -87,9 +87,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-compatibility-test.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-compatibility-test.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-compatibility-test.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-compatibility-test.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-compatibility-test" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-compatibility-test" + @echo "# mkdir -p $$HOME/.local/share/devhelp/certbot-compatibility-test" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-compatibility-test" @echo "# devhelp" epub: diff --git a/letsencrypt-compatibility-test/docs/_static/.gitignore b/certbot-compatibility-test/docs/_static/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/_static/.gitignore rename to certbot-compatibility-test/docs/_static/.gitignore diff --git a/letsencrypt-compatibility-test/docs/_templates/.gitignore b/certbot-compatibility-test/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/_templates/.gitignore rename to certbot-compatibility-test/docs/_templates/.gitignore diff --git a/letsencrypt-compatibility-test/docs/api.rst b/certbot-compatibility-test/docs/api.rst similarity index 100% rename from letsencrypt-compatibility-test/docs/api.rst rename to certbot-compatibility-test/docs/api.rst diff --git a/certbot-compatibility-test/docs/api/index.rst b/certbot-compatibility-test/docs/api/index.rst new file mode 100644 index 000000000..fea92d2e5 --- /dev/null +++ b/certbot-compatibility-test/docs/api/index.rst @@ -0,0 +1,53 @@ +:mod:`certbot_compatibility_test` +------------------------------------- + +.. automodule:: certbot_compatibility_test + :members: + +:mod:`certbot_compatibility_test.errors` +============================================ + +.. automodule:: certbot_compatibility_test.errors + :members: + +:mod:`certbot_compatibility_test.interfaces` +================================================ + +.. automodule:: certbot_compatibility_test.interfaces + :members: + +:mod:`certbot_compatibility_test.test_driver` +================================================= + +.. automodule:: certbot_compatibility_test.test_driver + :members: + +:mod:`certbot_compatibility_test.util` +========================================== + +.. automodule:: certbot_compatibility_test.util + :members: + +:mod:`certbot_compatibility_test.configurators` +=================================================== + +.. automodule:: certbot_compatibility_test.configurators + :members: + +:mod:`certbot_compatibility_test.configurators.apache` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: certbot_compatibility_test.configurators.apache + :members: + +:mod:`certbot_compatibility_test.configurators.apache.apache24` +------------------------------------------------------------------- + +.. automodule:: certbot_compatibility_test.configurators.apache.apache24 + :members: + +:mod:`certbot_compatibility_test.configurators.apache.common` +------------------------------------------------------------------- + +.. automodule:: certbot_compatibility_test.configurators.apache.common + :members: diff --git a/letsencrypt-compatibility-test/docs/conf.py b/certbot-compatibility-test/docs/conf.py similarity index 92% rename from letsencrypt-compatibility-test/docs/conf.py rename to certbot-compatibility-test/docs/conf.py index 3ee161efb..1ef69ab2d 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/certbot-compatibility-test/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-compatibility-test documentation build configuration file, created by +# certbot-compatibility-test documentation build configuration file, created by # sphinx-quickstart on Sun Oct 18 13:40:53 2015. # # This file is execfile()d with the current directory set to its @@ -59,9 +59,9 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-compatibility-test' +project = u'certbot-compatibility-test' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -221,7 +221,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-compatibility-testdoc' +htmlhelp_basename = 'certbot-compatibility-testdoc' # -- Options for LaTeX output --------------------------------------------- @@ -243,9 +243,9 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letsencrypt-compatibility-test.tex', - u'letsencrypt-compatibility-test Documentation', - u'Let\'s Encrypt Project', 'manual'), + (master_doc, 'certbot-compatibility-test.tex', + u'certbot-compatibility-test Documentation', + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -274,8 +274,8 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'letsencrypt-compatibility-test', - u'letsencrypt-compatibility-test Documentation', + (master_doc, 'certbot-compatibility-test', + u'certbot-compatibility-test Documentation', [author], 1) ] @@ -289,9 +289,9 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'letsencrypt-compatibility-test', - u'letsencrypt-compatibility-test Documentation', - author, 'letsencrypt-compatibility-test', + (master_doc, 'certbot-compatibility-test', + u'certbot-compatibility-test Documentation', + author, 'certbot-compatibility-test', 'One line description of project.', 'Miscellaneous'), ] @@ -311,9 +311,9 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), - 'letsencrypt-apache': ( + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot-apache': ( 'https://letsencrypt-apache.readthedocs.org/en/latest/', None), - 'letsencrypt-nginx': ( + 'certbot-nginx': ( 'https://letsencrypt-nginx.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-compatibility-test/docs/index.rst b/certbot-compatibility-test/docs/index.rst similarity index 75% rename from letsencrypt-compatibility-test/docs/index.rst rename to certbot-compatibility-test/docs/index.rst index df57ee6e6..a5e71e844 100644 --- a/letsencrypt-compatibility-test/docs/index.rst +++ b/certbot-compatibility-test/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-compatibility-test documentation master file, created by +.. certbot-compatibility-test documentation master file, created by sphinx-quickstart on Sun Oct 18 13:40:53 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to letsencrypt-compatibility-test's documentation! +Welcome to certbot-compatibility-test's documentation! ========================================================== Contents: diff --git a/letsencrypt-compatibility-test/docs/make.bat b/certbot-compatibility-test/docs/make.bat similarity index 97% rename from letsencrypt-compatibility-test/docs/make.bat rename to certbot-compatibility-test/docs/make.bat index c75269bdc..b6c0360f4 100644 --- a/letsencrypt-compatibility-test/docs/make.bat +++ b/certbot-compatibility-test/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-compatibility-test.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-compatibility-test.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-compatibility-test.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-compatibility-test.ghc goto end ) diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/certbot-compatibility-test/readthedocs.org.requirements.txt similarity index 88% rename from letsencrypt-compatibility-test/readthedocs.org.requirements.txt rename to certbot-compatibility-test/readthedocs.org.requirements.txt index 957a8a157..c2a0c1110 100644 --- a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt +++ b/certbot-compatibility-test/readthedocs.org.requirements.txt @@ -9,5 +9,5 @@ -e acme -e . --e letsencrypt-apache --e letsencrypt-compatibility-test[docs] +-e certbot-apache +-e certbot-compatibility-test[docs] diff --git a/letsencrypt-compatibility-test/setup.py b/certbot-compatibility-test/setup.py similarity index 80% rename from letsencrypt-compatibility-test/setup.py rename to certbot-compatibility-test/setup.py index 67262ba72..c62a10f89 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,11 +4,11 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.6.0.dev0' install_requires = [ - 'letsencrypt=={0}'.format(version), - 'letsencrypt-apache=={0}'.format(version), + 'certbot=={0}'.format(version), + 'certbot-apache=={0}'.format(version), 'docker-py', 'requests', 'zope.interface', @@ -31,11 +31,11 @@ docs_extras = [ ] setup( - name='letsencrypt-compatibility-test', + name='certbot-compatibility-test', version=version, - description="Compatibility tests for Let's Encrypt client", + description="Compatibility tests for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ @@ -58,7 +58,7 @@ setup( }, entry_points={ 'console_scripts': [ - 'letsencrypt-compatibility-test = letsencrypt_compatibility_test.test_driver:main', + 'certbot-compatibility-test = certbot_compatibility_test.test_driver:main', ], }, ) diff --git a/certbot-nginx/LICENSE.txt b/certbot-nginx/LICENSE.txt new file mode 100644 index 000000000..02a1459be --- /dev/null +++ b/certbot-nginx/LICENSE.txt @@ -0,0 +1,216 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Incorporating code from nginxparser + Copyright 2014 Fatih Erikli + Licensed MIT + + +Text of Apache License +====================== + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +Text of MIT License +=================== +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/certbot-nginx/MANIFEST.in b/certbot-nginx/MANIFEST.in new file mode 100644 index 000000000..2daca6738 --- /dev/null +++ b/certbot-nginx/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include certbot_nginx/tests/testdata * +include certbot_nginx/options-ssl-nginx.conf diff --git a/certbot-nginx/README.rst b/certbot-nginx/README.rst new file mode 100644 index 000000000..69d73ca3c --- /dev/null +++ b/certbot-nginx/README.rst @@ -0,0 +1 @@ +Nginx plugin for Certbot diff --git a/certbot-nginx/certbot_nginx/__init__.py b/certbot-nginx/certbot_nginx/__init__.py new file mode 100644 index 000000000..d4491dd9a --- /dev/null +++ b/certbot-nginx/certbot_nginx/__init__.py @@ -0,0 +1 @@ +"""Certbot nginx plugin.""" diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py similarity index 96% rename from letsencrypt-nginx/letsencrypt_nginx/configurator.py rename to certbot-nginx/certbot_nginx/configurator.py index 3a45a2e0e..e402d5c79 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -13,19 +13,19 @@ import zope.interface from acme import challenges from acme import crypto_util as acme_crypto_util -from letsencrypt import constants as core_constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import reverter +from certbot import constants as core_constants +from certbot import crypto_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot import reverter -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_nginx import constants -from letsencrypt_nginx import tls_sni_01 -from letsencrypt_nginx import obj -from letsencrypt_nginx import parser +from certbot_nginx import constants +from certbot_nginx import tls_sni_01 +from certbot_nginx import obj +from certbot_nginx import parser logger = logging.getLogger(__name__) @@ -41,15 +41,15 @@ class NginxConfigurator(common.Plugin): config files modified by the configurator will lose all their comments. :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt_nginx.parser` + :type parser: :class:`~certbot_nginx.parser` :ivar str save_notes: Human-readable config change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.reverter.Reverter` + :type reverter: :class:`certbot.reverter.Reverter` :ivar tup version: version of Nginx @@ -216,7 +216,7 @@ class NginxConfigurator(common.Plugin): :param str target_name: domain name :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt_nginx.obj.VirtualHost` + :rtype: :class:`~certbot_nginx.obj.VirtualHost` """ vhost = None @@ -333,7 +333,7 @@ class NginxConfigurator(common.Plugin): the existing one? :param vhost: The vhost to add SSL to. - :type vhost: :class:`~letsencrypt_nginx.obj.VirtualHost` + :type vhost: :class:`~certbot_nginx.obj.VirtualHost` """ snakeoil_cert, snakeoil_key = self._get_snakeoil_paths() @@ -372,9 +372,9 @@ class NginxConfigurator(common.Plugin): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.constants.ENHANCEMENTS` + See :const:`~certbot.constants.ENHANCEMENTS` documentation for appropriate parameter. """ @@ -395,7 +395,7 @@ class NginxConfigurator(common.Plugin): .. note:: This function saves the configuration :param vhost: Destination of traffic, an ssl enabled vhost - :type vhost: :class:`~letsencrypt_nginx.obj.VirtualHost` + :type vhost: :class:`~certbot_nginx.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available diff --git a/letsencrypt-nginx/letsencrypt_nginx/constants.py b/certbot-nginx/certbot_nginx/constants.py similarity index 73% rename from letsencrypt-nginx/letsencrypt_nginx/constants.py rename to certbot-nginx/certbot_nginx/constants.py index 08b205d2a..5dde30efc 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/constants.py +++ b/certbot-nginx/certbot_nginx/constants.py @@ -13,6 +13,6 @@ MOD_SSL_CONF_DEST = "options-ssl-nginx.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" MOD_SSL_CONF_SRC = pkg_resources.resource_filename( - "letsencrypt_nginx", "options-ssl-nginx.conf") -"""Path to the nginx mod_ssl config file found in the Let's Encrypt + "certbot_nginx", "options-ssl-nginx.conf") +"""Path to the nginx mod_ssl config file found in the Certbot distribution.""" diff --git a/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/nginxparser.py rename to certbot-nginx/certbot_nginx/nginxparser.py diff --git a/letsencrypt-nginx/letsencrypt_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py similarity index 99% rename from letsencrypt-nginx/letsencrypt_nginx/obj.py rename to certbot-nginx/certbot_nginx/obj.py index 421c676b6..0d1151f39 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -1,7 +1,7 @@ """Module contains classes used by the Nginx Configurator.""" import re -from letsencrypt.plugins import common +from certbot.plugins import common class Addr(common.Addr): diff --git a/letsencrypt-nginx/letsencrypt_nginx/options-ssl-nginx.conf b/certbot-nginx/certbot_nginx/options-ssl-nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/options-ssl-nginx.conf rename to certbot-nginx/certbot_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py similarity index 99% rename from letsencrypt-nginx/letsencrypt_nginx/parser.py rename to certbot-nginx/certbot_nginx/parser.py index 3b1dd049e..2f08c15d3 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -5,10 +5,10 @@ import os import pyparsing import re -from letsencrypt import errors +from certbot import errors -from letsencrypt_nginx import obj -from letsencrypt_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import nginxparser logger = logging.getLogger(__name__) @@ -87,7 +87,7 @@ class NginxParser(object): Technically this is a misnomer because Nginx does not have virtual hosts, it has 'server blocks'. - :returns: List of :class:`~letsencrypt_nginx.obj.VirtualHost` + :returns: List of :class:`~certbot_nginx.obj.VirtualHost` objects found in configuration :rtype: list diff --git a/certbot-nginx/certbot_nginx/tests/__init__.py b/certbot-nginx/certbot_nginx/tests/__init__.py new file mode 100644 index 000000000..32ca193d9 --- /dev/null +++ b/certbot-nginx/certbot_nginx/tests/__init__.py @@ -0,0 +1 @@ +"""Certbot Nginx Tests""" diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py similarity index 93% rename from letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py rename to certbot-nginx/certbot_nginx/tests/configurator_test.py index 4d15d6a75..b36802939 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -1,5 +1,5 @@ # pylint: disable=too-many-public-methods -"""Test for letsencrypt_nginx.configurator.""" +"""Test for certbot_nginx.configurator.""" import os import shutil import unittest @@ -10,10 +10,10 @@ import OpenSSL from acme import challenges from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt_nginx.tests import util +from certbot_nginx.tests import util class NginxConfiguratorTest(util.NginxTest): @@ -30,7 +30,7 @@ class NginxConfiguratorTest(util.NginxTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists") + @mock.patch("certbot_nginx.configurator.le_util.exe_exists") def test_prepare_no_install(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( @@ -40,8 +40,8 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEquals((1, 6, 2), self.config.version) self.assertEquals(5, len(self.config.parser.parsed)) - @mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists") - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.le_util.exe_exists") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_prepare_initializes_version(self, mock_popen, mock_exe_exists): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.6.2", @@ -58,7 +58,7 @@ class NginxConfiguratorTest(util.NginxTest): self.config.prepare() self.assertEquals((1, 6, 2), self.config.version) - @mock.patch("letsencrypt_nginx.configurator.socket.gethostbyaddr") + @mock.patch("certbot_nginx.configurator.socket.gethostbyaddr") def test_get_all_names(self, mock_gethostbyaddr): mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], []) names = self.config.get_all_names() @@ -263,8 +263,8 @@ class NginxConfiguratorTest(util.NginxTest): ('/etc/nginx/fullchain.pem', '/etc/nginx/key.pem', nginx_conf), ]), self.config.get_all_certs_keys()) - @mock.patch("letsencrypt_nginx.configurator.tls_sni_01.NginxTlsSni01.perform") - @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.restart") + @mock.patch("certbot_nginx.configurator.tls_sni_01.NginxTlsSni01.perform") + @mock.patch("certbot_nginx.configurator.NginxConfigurator.restart") def test_perform(self, mock_restart, mock_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -293,7 +293,7 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEqual(responses, expected) self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", @@ -343,55 +343,55 @@ class NginxConfiguratorTest(util.NginxTest): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 0 self.config.restart() - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart_fail(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 1 self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_no_nginx_start(self, mock_popen): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test(self, _): self.config.config_test() - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test_bad_process(self, mock_run_script): mock_run_script.side_effect = errors.SubprocessError self.assertRaises(errors.MisconfigurationError, self.config.config_test) - @mock.patch("letsencrypt.reverter.Reverter.recovery_routine") + @mock.patch("certbot.reverter.Reverter.recovery_routine") def test_recovery_routine_throws_error_from_reverter(self, mock_recovery_routine): mock_recovery_routine.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.recovery_routine) - @mock.patch("letsencrypt.reverter.Reverter.view_config_changes") + @mock.patch("certbot.reverter.Reverter.view_config_changes") def test_view_config_changes_throws_error_from_reverter(self, mock_view_config_changes): mock_view_config_changes.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.view_config_changes) - @mock.patch("letsencrypt.reverter.Reverter.rollback_checkpoints") + @mock.patch("certbot.reverter.Reverter.rollback_checkpoints") def test_rollback_checkpoints_throws_error_from_reverter(self, mock_rollback_checkpoints): mock_rollback_checkpoints.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.rollback_checkpoints) - @mock.patch("letsencrypt.reverter.Reverter.revert_temporary_config") + @mock.patch("certbot.reverter.Reverter.revert_temporary_config") def test_revert_challenge_config_throws_error_from_reverter(self, mock_revert_temporary_config): mock_revert_temporary_config.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.revert_challenge_config) - @mock.patch("letsencrypt.reverter.Reverter.add_to_checkpoint") + @mock.patch("certbot.reverter.Reverter.add_to_checkpoint") def test_save_throws_error_from_reverter(self, mock_add_to_checkpoint): mock_add_to_checkpoint.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.save) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py b/certbot-nginx/certbot_nginx/tests/nginxparser_test.py similarity index 98% rename from letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py rename to certbot-nginx/certbot_nginx/tests/nginxparser_test.py index 2130b4824..80e82c903 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py +++ b/certbot-nginx/certbot_nginx/tests/nginxparser_test.py @@ -1,12 +1,12 @@ -"""Test for letsencrypt_nginx.nginxparser.""" +"""Test for certbot_nginx.nginxparser.""" import operator import unittest from pyparsing import ParseException -from letsencrypt_nginx.nginxparser import ( +from certbot_nginx.nginxparser import ( RawNginxParser, loads, load, dumps, dump) -from letsencrypt_nginx.tests import util +from certbot_nginx.tests import util FIRST = operator.itemgetter(0) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py similarity index 89% rename from letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py rename to certbot-nginx/certbot_nginx/tests/obj_test.py index e3c22b49d..e7a993d1b 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py +++ b/certbot-nginx/certbot_nginx/tests/obj_test.py @@ -1,11 +1,11 @@ -"""Test the helper objects in letsencrypt_nginx.obj.""" +"""Test the helper objects in certbot_nginx.obj.""" import unittest class AddrTest(unittest.TestCase): """Test the Addr class.""" def setUp(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:* ssl") self.addr3 = Addr.fromstring("192.168.1.1:80") @@ -56,14 +56,14 @@ class AddrTest(unittest.TestCase): self.assertEqual(str(self.addr6), "80 default_server") def test_eq(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr new_addr1 = Addr.fromstring("192.168.1.1 spdy") self.assertEqual(self.addr1, new_addr1) self.assertNotEqual(self.addr1, self.addr2) self.assertFalse(self.addr1 == 3333) def test_set_inclusion(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:* ssl") @@ -75,16 +75,16 @@ class AddrTest(unittest.TestCase): class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): - from letsencrypt_nginx.obj import VirtualHost - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import VirtualHost + from certbot_nginx.obj import Addr self.vhost1 = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), []) def test_eq(self): - from letsencrypt_nginx.obj import Addr - from letsencrypt_nginx.obj import VirtualHost + from certbot_nginx.obj import Addr + from certbot_nginx.obj import VirtualHost vhost1b = VirtualHost( "filep", set([Addr.fromstring("localhost blah")]), False, False, diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py similarity index 98% rename from letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py rename to certbot-nginx/certbot_nginx/tests/parser_test.py index b597fcad5..8ac995dfc 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -1,16 +1,16 @@ -"""Tests for letsencrypt_nginx.parser.""" +"""Tests for certbot_nginx.parser.""" import glob import os import re import shutil import unittest -from letsencrypt import errors +from certbot import errors -from letsencrypt_nginx import nginxparser -from letsencrypt_nginx import obj -from letsencrypt_nginx import parser -from letsencrypt_nginx.tests import util +from certbot_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import parser +from certbot_nginx.tests import util class NginxParserTest(util.NginxTest): diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/broken.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/broken.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/broken.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/broken.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/edge_cases.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/edge_cases.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/foo.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/foo.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/foo.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/foo.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/mime.types b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/mime.types similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/mime.types rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/mime.types diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.new.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.new.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.new.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.new.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/server.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/server.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/server.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/server.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/example.com b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/example.com rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py b/certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py similarity index 92% rename from letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py rename to certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py index 04fe01bc4..3264d6ed3 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py +++ b/certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_nginx.tls_sni_01""" +"""Tests for certbot_nginx.tls_sni_01""" import unittest import shutil @@ -6,14 +6,14 @@ import mock from acme import challenges -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.plugins import common_test -from letsencrypt.tests import acme_util +from certbot.plugins import common_test +from certbot.tests import acme_util -from letsencrypt_nginx import obj -from letsencrypt_nginx.tests import util +from certbot_nginx import obj +from certbot_nginx.tests import util class TlsSniPerformTest(util.NginxTest): @@ -47,7 +47,7 @@ class TlsSniPerformTest(util.NginxTest): config = util.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir) - from letsencrypt_nginx import tls_sni_01 + from certbot_nginx import tls_sni_01 self.sni = tls_sni_01.NginxTlsSni01(config) def tearDown(self): @@ -55,7 +55,7 @@ class TlsSniPerformTest(util.NginxTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_nginx.configurator" + @mock.patch("certbot_nginx.configurator" ".NginxConfigurator.choose_vhost") def test_perform(self, mock_choose): self.sni.add_chall(self.achalls[1]) @@ -67,7 +67,7 @@ class TlsSniPerformTest(util.NginxTest): responses = self.sni.perform() self.assertEqual([], responses) - @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.save") + @mock.patch("certbot_nginx.configurator.NginxConfigurator.save") def test_perform1(self, mock_save): self.sni.add_chall(self.achalls[0]) response = self.achalls[0].response(self.account_key) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py b/certbot-nginx/certbot_nginx/tests/util.py similarity index 86% rename from letsencrypt-nginx/letsencrypt_nginx/tests/util.py rename to certbot-nginx/certbot_nginx/tests/util.py index 7a16e3738..3c4731700 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py +++ b/certbot-nginx/certbot_nginx/tests/util.py @@ -1,4 +1,4 @@ -"""Common utilities for letsencrypt_nginx.""" +"""Common utilities for certbot_nginx.""" import os import pkg_resources import unittest @@ -8,14 +8,14 @@ import zope.component from acme import jose -from letsencrypt import configuration +from certbot import configuration -from letsencrypt.tests import test_util +from certbot.tests import test_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_nginx import constants -from letsencrypt_nginx import configurator +from certbot_nginx import constants +from certbot_nginx import configurator class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -24,7 +24,7 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods super(NginxTest, self).setUp() self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( - "etc_nginx", "letsencrypt_nginx.tests") + "etc_nginx", "certbot_nginx.tests") self.ssl_options = common.setup_ssl_options( self.config_dir, constants.MOD_SSL_CONF_SRC, @@ -39,7 +39,7 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods def get_data_filename(filename): """Gets the filename of a test data file.""" return pkg_resources.resource_filename( - "letsencrypt_nginx.tests", os.path.join( + "certbot_nginx.tests", os.path.join( "testdata", "etc_nginx", filename)) @@ -49,9 +49,9 @@ def get_nginx_configurator( backups = os.path.join(work_dir, "backups") - with mock.patch("letsencrypt_nginx.configurator.NginxConfigurator." + with mock.patch("certbot_nginx.configurator.NginxConfigurator." "config_test"): - with mock.patch("letsencrypt_nginx.configurator.le_util." + with mock.patch("certbot_nginx.configurator.le_util." "exe_exists") as mock_exe_exists: mock_exe_exists.return_value = True config = configurator.NginxConfigurator( diff --git a/letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py similarity index 92% rename from letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py rename to certbot-nginx/certbot_nginx/tls_sni_01.py index e59281c4c..e4c5d31a6 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py +++ b/certbot-nginx/certbot_nginx/tls_sni_01.py @@ -4,11 +4,11 @@ import itertools import logging import os -from letsencrypt import errors -from letsencrypt.plugins import common +from certbot import errors +from certbot.plugins import common -from letsencrypt_nginx import obj -from letsencrypt_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import nginxparser logger = logging.getLogger(__name__) @@ -21,7 +21,7 @@ class NginxTlsSni01(common.TLSSNI01): :type configurator: :class:`~nginx.configurator.NginxConfigurator` :ivar list achalls: Annotated - class:`~letsencrypt.achallenges.KeyAuthorizationAnnotatedChallenge` + class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge` challenges :param list indices: Meant to hold indices of challenges in a @@ -39,7 +39,7 @@ class NginxTlsSni01(common.TLSSNI01): def perform(self): """Perform a challenge on Nginx. - :returns: list of :class:`letsencrypt.acme.challenges.TLSSNI01Response` + :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response` :rtype: list """ @@ -83,7 +83,7 @@ class NginxTlsSni01(common.TLSSNI01): """Modifies Nginx config to include challenge server blocks. :param list ll_addrs: list of lists of - :class:`letsencrypt_nginx.obj.Addr` to apply + :class:`certbot_nginx.obj.Addr` to apply :raises .MisconfigurationError: Unable to find a suitable HTTP block in which to include @@ -130,7 +130,7 @@ class NginxTlsSni01(common.TLSSNI01): :param achall: Annotated TLS-SNI-01 challenge :type achall: - :class:`letsencrypt.achallenges.KeyAuthorizationAnnotatedChallenge` + :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge` :param list addrs: addresses of challenged domain :class:`list` of type :class:`~nginx.obj.Addr` diff --git a/letsencrypt-nginx/docs/.gitignore b/certbot-nginx/docs/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/.gitignore rename to certbot-nginx/docs/.gitignore diff --git a/letsencrypt-apache/docs/Makefile b/certbot-nginx/docs/Makefile similarity index 96% rename from letsencrypt-apache/docs/Makefile rename to certbot-nginx/docs/Makefile index 9bf5154fe..0bd88a347 100644 --- a/letsencrypt-apache/docs/Makefile +++ b/certbot-nginx/docs/Makefile @@ -87,9 +87,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-apache.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-nginx.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-apache.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-nginx.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-apache" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-apache" + @echo "# mkdir -p $$HOME/.local/share/devhelp/certbot-nginx" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-nginx" @echo "# devhelp" epub: diff --git a/letsencrypt-nginx/docs/_static/.gitignore b/certbot-nginx/docs/_static/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/_static/.gitignore rename to certbot-nginx/docs/_static/.gitignore diff --git a/letsencrypt-nginx/docs/_templates/.gitignore b/certbot-nginx/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/_templates/.gitignore rename to certbot-nginx/docs/_templates/.gitignore diff --git a/letsencrypt-nginx/docs/api.rst b/certbot-nginx/docs/api.rst similarity index 100% rename from letsencrypt-nginx/docs/api.rst rename to certbot-nginx/docs/api.rst diff --git a/certbot-nginx/docs/api/nginxparser.rst b/certbot-nginx/docs/api/nginxparser.rst new file mode 100644 index 000000000..6a3be5247 --- /dev/null +++ b/certbot-nginx/docs/api/nginxparser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.nginxparser` +------------------------------------ + +.. automodule:: certbot_nginx.nginxparser + :members: diff --git a/certbot-nginx/docs/api/obj.rst b/certbot-nginx/docs/api/obj.rst new file mode 100644 index 000000000..a2c94037b --- /dev/null +++ b/certbot-nginx/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.obj` +---------------------------- + +.. automodule:: certbot_nginx.obj + :members: diff --git a/certbot-nginx/docs/api/parser.rst b/certbot-nginx/docs/api/parser.rst new file mode 100644 index 000000000..0149f99cb --- /dev/null +++ b/certbot-nginx/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.parser` +------------------------------- + +.. automodule:: certbot_nginx.parser + :members: diff --git a/certbot-nginx/docs/api/tls_sni_01.rst b/certbot-nginx/docs/api/tls_sni_01.rst new file mode 100644 index 000000000..5074f63d9 --- /dev/null +++ b/certbot-nginx/docs/api/tls_sni_01.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.tls_sni_01` +----------------------------------- + +.. automodule:: certbot_nginx.tls_sni_01 + :members: diff --git a/letsencrypt-nginx/docs/conf.py b/certbot-nginx/docs/conf.py similarity index 94% rename from letsencrypt-nginx/docs/conf.py rename to certbot-nginx/docs/conf.py index 14713a4b2..fa00e6503 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/certbot-nginx/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-nginx documentation build configuration file, created by +# certbot-nginx documentation build configuration file, created by # sphinx-quickstart on Sun Oct 18 13:39:39 2015. # # This file is execfile()d with the current directory set to its @@ -58,7 +58,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-nginx' +project = u'certbot-nginx' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' @@ -220,7 +220,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-nginxdoc' +htmlhelp_basename = 'certbot-nginxdoc' # -- Options for LaTeX output --------------------------------------------- @@ -242,7 +242,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letsencrypt-nginx.tex', u'letsencrypt-nginx Documentation', + (master_doc, 'certbot-nginx.tex', u'certbot-nginx Documentation', u'Let\'s Encrypt Project', 'manual'), ] @@ -272,7 +272,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'letsencrypt-nginx', u'letsencrypt-nginx Documentation', + (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', [author], 1) ] @@ -286,8 +286,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'letsencrypt-nginx', u'letsencrypt-nginx Documentation', - author, 'letsencrypt-nginx', 'One line description of project.', + (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', + author, 'certbot-nginx', 'One line description of project.', 'Miscellaneous'), ] @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-nginx/docs/index.rst b/certbot-nginx/docs/index.rst similarity index 74% rename from letsencrypt-nginx/docs/index.rst rename to certbot-nginx/docs/index.rst index e4f8f715f..488a7ab9c 100644 --- a/letsencrypt-nginx/docs/index.rst +++ b/certbot-nginx/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-nginx documentation master file, created by +.. certbot-nginx documentation master file, created by sphinx-quickstart on Sun Oct 18 13:39:39 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to letsencrypt-nginx's documentation! +Welcome to certbot-nginx's documentation! ============================================= Contents: @@ -18,7 +18,7 @@ Contents: api -.. automodule:: letsencrypt_nginx +.. automodule:: certbot_nginx :members: diff --git a/letsencrypt-apache/docs/make.bat b/certbot-nginx/docs/make.bat similarity index 97% rename from letsencrypt-apache/docs/make.bat rename to certbot-nginx/docs/make.bat index 62a54fd2c..b12255d4c 100644 --- a/letsencrypt-apache/docs/make.bat +++ b/certbot-nginx/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-apache.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-nginx.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-apache.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-nginx.ghc goto end ) diff --git a/letsencrypt-apache/readthedocs.org.requirements.txt b/certbot-nginx/readthedocs.org.requirements.txt similarity index 94% rename from letsencrypt-apache/readthedocs.org.requirements.txt rename to certbot-nginx/readthedocs.org.requirements.txt index 7855b5ce2..ca5f33363 100644 --- a/letsencrypt-apache/readthedocs.org.requirements.txt +++ b/certbot-nginx/readthedocs.org.requirements.txt @@ -9,4 +9,4 @@ -e acme -e . --e letsencrypt-apache[docs] +-e certbot-nginx[docs] diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py new file mode 100644 index 000000000..0e5c27a0a --- /dev/null +++ b/certbot-nginx/setup.py @@ -0,0 +1,69 @@ +import sys + +from setuptools import setup +from setuptools import find_packages + + +version = '0.6.0.dev0' + +# Please update tox.ini when modifying dependency version requirements +install_requires = [ + 'acme=={0}'.format(version), + 'certbot=={0}'.format(version), + 'PyOpenSSL', + 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? + # For pkg_resources. >=1.0 so pip resolves it to a version cryptography + # will tolerate; see #2599: + 'setuptools>=1.0', + 'zope.interface', +] + +if sys.version_info < (2, 7): + install_requires.append('mock<1.1.0') +else: + install_requires.append('mock') + +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + +setup( + name='certbot-nginx', + version=version, + description="Nginx plugin for Certbot", + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, + entry_points={ + 'certbot.plugins': [ + 'nginx = certbot_nginx.configurator:NginxConfigurator', + ], + }, + test_suite='certbot_nginx', +) diff --git a/letsencrypt-nginx/tests/boulder-integration.conf.sh b/certbot-nginx/tests/boulder-integration.conf.sh similarity index 100% rename from letsencrypt-nginx/tests/boulder-integration.conf.sh rename to certbot-nginx/tests/boulder-integration.conf.sh diff --git a/letsencrypt-nginx/tests/boulder-integration.sh b/certbot-nginx/tests/boulder-integration.sh similarity index 76% rename from letsencrypt-nginx/tests/boulder-integration.sh rename to certbot-nginx/tests/boulder-integration.sh index 3cbe9f6b9..bd35aee21 100755 --- a/letsencrypt-nginx/tests/boulder-integration.sh +++ b/certbot-nginx/tests/boulder-integration.sh @@ -6,19 +6,19 @@ export PATH="/usr/sbin:$PATH" # /usr/sbin/nginx nginx_root="$root/nginx" mkdir $nginx_root -root="$nginx_root" ./letsencrypt-nginx/tests/boulder-integration.conf.sh > $nginx_root/nginx.conf +root="$nginx_root" ./certbot-nginx/tests/boulder-integration.conf.sh > $nginx_root/nginx.conf killall nginx || true nginx -c $nginx_root/nginx.conf -letsencrypt_test_nginx () { - letsencrypt_test \ +certbot_test_nginx () { + certbot_test \ --configurator nginx \ --nginx-server-root $nginx_root \ "$@" } -letsencrypt_test_nginx --domains nginx.wtf run +certbot_test_nginx --domains nginx.wtf run echo | openssl s_client -connect localhost:5001 \ | openssl x509 -out $root/nginx.pem diff -q $root/nginx.pem $root/conf/live/nginx.wtf/cert.pem diff --git a/letsencrypt/.gitignore b/certbot/.gitignore similarity index 100% rename from letsencrypt/.gitignore rename to certbot/.gitignore diff --git a/letsencrypt/__init__.py b/certbot/__init__.py similarity index 55% rename from letsencrypt/__init__.py rename to certbot/__init__.py index 0dbeb1567..a48d62548 100644 --- a/letsencrypt/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client.""" +"""Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.5.0.dev0' +__version__ = '0.6.0.dev0' diff --git a/letsencrypt/account.py b/certbot/account.py similarity index 94% rename from letsencrypt/account.py rename to certbot/account.py index c41b10c4a..cc50a6ea6 100644 --- a/letsencrypt/account.py +++ b/certbot/account.py @@ -14,9 +14,9 @@ from acme import fields as acme_fields from acme import jose from acme import messages -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) @@ -81,15 +81,15 @@ class Account(object): # pylint: disable=too-few-public-methods def report_new_account(acc, config): - """Informs the user about their new Let's Encrypt account.""" + """Informs the user about their new ACME account.""" reporter = zope.component.queryUtility(interfaces.IReporter) if reporter is None: return reporter.add_message( - "Your account credentials have been saved in your Let's Encrypt " + "Your account credentials have been saved in your Certbot " "configuration directory at {0}. You should make a secure backup " "of this folder now. This configuration directory will also " - "contain certificates and private keys obtained by Let's Encrypt " + "contain certificates and private keys obtained by Certbot " "so making regular backups of this folder is ideal.".format( config.config_dir), reporter.MEDIUM_PRIORITY) @@ -98,7 +98,7 @@ def report_new_account(acc, config): recovery_msg = ("If you lose your account credentials, you can " "recover through e-mails sent to {0}.".format( ", ".join(acc.regr.body.emails))) - reporter.add_message(recovery_msg, reporter.HIGH_PRIORITY) + reporter.add_message(recovery_msg, reporter.MEDIUM_PRIORITY) class AccountMemoryStorage(interfaces.AccountStorage): diff --git a/letsencrypt/achallenges.py b/certbot/achallenges.py similarity index 97% rename from letsencrypt/achallenges.py rename to certbot/achallenges.py index 0cdec06df..5ee6d2945 100644 --- a/letsencrypt/achallenges.py +++ b/certbot/achallenges.py @@ -6,7 +6,7 @@ and :class:`.ChallengeBody` (denoted by ``challb``):: from acme import challenges from acme import messages - from letsencrypt import achallenges + from certbot import achallenges chall = challenges.DNS(token='foo') challb = messages.ChallengeBody(chall=chall) diff --git a/letsencrypt/auth_handler.py b/certbot/auth_handler.py similarity index 95% rename from letsencrypt/auth_handler.py rename to certbot/auth_handler.py index 658315597..f5557d604 100644 --- a/letsencrypt/auth_handler.py +++ b/certbot/auth_handler.py @@ -8,10 +8,10 @@ import zope.component from acme import challenges from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import interfaces +from certbot import achallenges +from certbot import errors +from certbot import error_handler +from certbot import interfaces logger = logging.getLogger(__name__) @@ -22,17 +22,17 @@ class AuthHandler(object): :ivar auth: Authenticator capable of solving :class:`~acme.challenges.Challenge` types - :type auth: :class:`letsencrypt.interfaces.IAuthenticator` + :type auth: :class:`certbot.interfaces.IAuthenticator` :ivar acme.client.Client acme: ACME client API. :ivar account: Client's Account - :type account: :class:`letsencrypt.account.Account` + :type account: :class:`certbot.account.Account` :ivar dict authzr: ACME Authorization Resource dict where keys are domains and values are :class:`acme.messages.AuthorizationResource` :ivar list achalls: DV challenges in the form of - :class:`letsencrypt.achallenges.AnnotatedChallenge` + :class:`certbot.achallenges.AnnotatedChallenge` """ def __init__(self, auth, acme, account): @@ -287,7 +287,7 @@ class AuthHandler(object): :param list path: List of indices from `challenges`. :returns: achalls, list of challenge type - :class:`letsencrypt.achallenges.Indexed` + :class:`certbot.achallenges.Indexed` :rtype: list :raises .errors.Error: if challenge type is not recognized @@ -310,7 +310,7 @@ def challb_to_achall(challb, account_key, domain): :param str domain: Domain of the challb :returns: Appropriate AnnotatedChallenge - :rtype: :class:`letsencrypt.achallenges.AnnotatedChallenge` + :rtype: :class:`certbot.achallenges.AnnotatedChallenge` """ chall = challb.chall @@ -347,7 +347,7 @@ def gen_challenge_path(challbs, preferences, combinations): :returns: tuple of indices from ``challenges``. :rtype: tuple - :raises letsencrypt.errors.AuthorizationError: If a + :raises certbot.errors.AuthorizationError: If a path cannot be created that satisfies the CA given the preferences and combinations. @@ -445,7 +445,7 @@ _ERROR_HELP = { "your domain, please ensure that the signature is valid.", "malformed": "To fix these errors, please make sure that you did not provide any " - "invalid information to the client, and try running Let's Encrypt " + "invalid information to the client, and try running Certbot " "again.", "serverInternal": "Unfortunately, an error on the ACME server prevented you from completing " @@ -453,7 +453,7 @@ _ERROR_HELP = { "tls": _ERROR_HELP_COMMON + " Additionally, please check that you have an " "up-to-date TLS configuration that allows the server to communicate " - "with the Let's Encrypt client.", + "with the Certbot client.", "unauthorized": _ERROR_HELP_COMMON, "unknownHost": _ERROR_HELP_COMMON, } @@ -463,7 +463,7 @@ def _report_failed_challs(failed_achalls): """Notifies the user about failed challenges. :param set failed_achalls: A set of failed - :class:`letsencrypt.achallenges.AnnotatedChallenge`. + :class:`certbot.achallenges.AnnotatedChallenge`. """ problems = dict() @@ -481,7 +481,7 @@ def _generate_failed_chall_msg(failed_achalls): """Creates a user friendly error message about failed challenges. :param list failed_achalls: A list of failed - :class:`letsencrypt.achallenges.AnnotatedChallenge` with the same error + :class:`certbot.achallenges.AnnotatedChallenge` with the same error type. :returns: A formatted error message for the client. diff --git a/letsencrypt/cli.py b/certbot/cli.py similarity index 74% rename from letsencrypt/cli.py rename to certbot/cli.py index 256e0c801..e2c57595b 100644 --- a/letsencrypt/cli.py +++ b/certbot/cli.py @@ -1,8 +1,7 @@ -"""Let's Encrypt command line argument & config processing.""" +"""Certbot command line argument & config processing.""" from __future__ import print_function import argparse import glob -import json import logging import logging.handlers import os @@ -13,16 +12,17 @@ import configargparse import OpenSSL import six -import letsencrypt +import certbot -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import hooks +from certbot import interfaces +from certbot import le_util -from letsencrypt.plugins import disco as plugins_disco -import letsencrypt.plugins.selection as plugin_selection +from certbot.plugins import disco as plugins_disco +import certbot.plugins.selection as plugin_selection logger = logging.getLogger(__name__) @@ -31,14 +31,14 @@ helpful_parser = None # For help strings, figure out how the user ran us. # When invoked from letsencrypt-auto, sys.argv[0] is something like: -# "/home/user/.local/share/letsencrypt/bin/letsencrypt" +# "/home/user/.local/share/certbot/bin/certbot" # Note that this won't work if the user set VENV_PATH or XDG_DATA_HOME before # running letsencrypt-auto (and sudo stops us from seeing if they did), so it # should only be used for purposes where inability to detect letsencrypt-auto # fails safely -fragment = os.path.join(".local", "share", "letsencrypt") -cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "letsencrypt" +fragment = os.path.join(".local", "share", "certbot") +cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot" # Argparse's help formatting has a lot of unhelpful peculiarities, so we want # to replace as much of it as we can... @@ -48,9 +48,9 @@ cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "letsencrypt" SHORT_USAGE = """ {0} [SUBCOMMAND] [options] [-d domain] [-d domain] ... -The Let's Encrypt agent can obtain and install HTTPS/TLS/SSL certificates. By -default, it will attempt to use a webserver both for obtaining and installing -the cert. Major SUBCOMMANDS are: +Certbot can obtain and install HTTPS/TLS/SSL certificates. By default, +it will attempt to use a webserver both for obtaining and installing the +cert. Major SUBCOMMANDS are: (default) run Obtain & install a cert in your current webserver certonly Obtain cert, but do not install it (aka "auth") @@ -63,7 +63,7 @@ the cert. Major SUBCOMMANDS are: """.format(cli_command) -# This is the short help for letsencrypt --help, where we disable argparse +# This is the short help for certbot --help, where we disable argparse # altogether USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing cert: @@ -86,6 +86,48 @@ More detailed help: """ +# These argparse parameters should be removed when detecting defaults. +ARGPARSE_PARAMS_TO_REMOVE = ("const", "nargs", "type",) + + +# These sets are used when to help detect options set by the user. +EXIT_ACTIONS = set(("help", "version",)) + + +ZERO_ARG_ACTIONS = set(("store_const", "store_true", + "store_false", "append_const", "count",)) + + +# Maps a config option to a set of config options that may have modified it. +# This dictionary is used recursively, so if A modifies B and B modifies C, +# it is determined that C was modified by the user if A was modified. +VAR_MODIFIERS = {"account": set(("server",)), + "server": set(("dry_run", "staging",)), + "webroot_map": set(("webroot_path",))} + + +def report_config_interaction(modified, modifiers): + """Registers config option interaction to be checked by set_by_cli. + + This function can be called by during the __init__ or + add_parser_arguments methods of plugins to register interactions + between config options. + + :param modified: config options that can be modified by modifiers + :type modified: iterable or str + :param modifiers: config options that modify modified + :type modifiers: iterable or str + + """ + if isinstance(modified, str): + modified = (modified,) + if isinstance(modifiers, str): + modifiers = (modifiers,) + + for var in modified: + VAR_MODIFIERS.setdefault(var, set()).update(modifiers) + + def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" if "nginx" in plugins: @@ -99,6 +141,22 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE +class _Default(object): + """A class to use as a default to detect if a value is set by a user""" + + def __bool__(self): + return False + + def __eq__(self, other): + return isinstance(other, _Default) + + def __hash__(self): + return id(_Default) + + def __nonzero__(self): + return self.__bool__() + + def set_by_cli(var): """ Return True if a particular config variable has been set by the user @@ -115,30 +173,18 @@ def set_by_cli(var): detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator - auth, inst = plugin_selection.cli_plugin_requests(detector) - detector.authenticator = auth if auth else "" - detector.installer = inst if inst else "" + detector.authenticator, detector.installer = ( + plugin_selection.cli_plugin_requests(detector)) logger.debug("Default Detector is %r", detector) - try: - # Is detector.var something that isn't false? - change_detected = getattr(detector, var) - except AttributeError: - logger.warning("Missing default analysis for %r", var) - return False + if not isinstance(getattr(detector, var), _Default): + return True - if change_detected: - return True - # Special case: we actually want account to be set to "" if the server - # the account was on has changed - elif var == "account" and (detector.server or detector.dry_run or detector.staging): - return True - # Special case: vars like --no-redirect that get set True -> False - # default to None; False means they were set - elif var in detector.store_false_vars and change_detected is not None: - return True - else: - return False + for modifier in VAR_MODIFIERS.get(var, []): + if set_by_cli(modifier): + return True + + return False # static housekeeping var set_by_cli.detector = None @@ -187,33 +233,35 @@ def config_help(name, hidden=False): return interfaces.IConfig[name].__doc__ -class SilentParser(object): # pylint: disable=too-few-public-methods - """Silent wrapper around argparse. +class HelpfulArgumentGroup(object): + """Emulates an argparse group for use with HelpfulArgumentParser. - A mini parser wrapper that doesn't print help for its - arguments. This is needed for the use of callbacks to define - arguments within plugins. + This class is used in the add_group method of HelpfulArgumentParser. + Command line arguments can be added to the group, but help + suppression and default detection is applied by + HelpfulArgumentParser when necessary. """ - def __init__(self, parser): - self.parser = parser + def __init__(self, helpful_arg_parser, topic): + self._parser = helpful_arg_parser + self._topic = topic def add_argument(self, *args, **kwargs): - """Wrap, but silence help""" - kwargs["help"] = argparse.SUPPRESS - self.parser.add_argument(*args, **kwargs) + """Add a new command line argument to the argument group.""" + self._parser.add(self._topic, *args, **kwargs) + class HelpfulArgumentParser(object): """Argparse Wrapper. This class wraps argparse, adding the ability to make --help less verbose, and request help on specific subcategories at a time, eg - 'letsencrypt --help security' for security options. + 'certbot --help security' for security options. """ def __init__(self, args, plugins, detect_defaults=False): - from letsencrypt import main + from certbot import main self.VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, "config_changes": main.config_changes, "run": main.run, "install": main.install, "plugins": main.plugins_cmd, @@ -234,15 +282,8 @@ class HelpfulArgumentParser(object): # This is the only way to turn off overly verbose config flag documentation self.parser._add_config_file_help = False # pylint: disable=protected-access - self.silent_parser = SilentParser(self.parser) - # This setting attempts to force all default values to things that are - # pythonically false; it is used to detect when values have been - # explicitly set by the user, including when they are set to their - # normal default value self.detect_defaults = detect_defaults - if detect_defaults: - self.store_false_vars = {} # vars that use "store_false" self.args = args self.determine_verb() @@ -268,21 +309,17 @@ class HelpfulArgumentParser(object): parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb - # Do any post-parsing homework here + if self.detect_defaults: + return parsed_args - # we get domains from -d, but also from the webroot map... - if parsed_args.webroot_map: - for domain in parsed_args.webroot_map.keys(): - if domain not in parsed_args.domains: - parsed_args.domains.append(domain) + # Do any post-parsing homework here if parsed_args.staging or parsed_args.dry_run: if parsed_args.server not in (flag_default("server"), constants.STAGING_URI): conflicts = ["--staging"] if parsed_args.staging else [] conflicts += ["--dry-run"] if parsed_args.dry_run else [] - if not self.detect_defaults: - raise errors.Error("--server value conflicts with {0}".format( - " and ".join(conflicts))) + raise errors.Error("--server value conflicts with {0}".format( + " and ".join(conflicts))) parsed_args.server = constants.STAGING_URI @@ -294,7 +331,7 @@ class HelpfulArgumentParser(object): if glob.glob(os.path.join(parsed_args.config_dir, constants.ACCOUNTS_DIR, "*")): # The user has a prod account, but might not have a staging # one; we don't want to start trying to perform interactive registration - parsed_args.agree_tos = True + parsed_args.tos = True parsed_args.register_unsafely_without_email = True if parsed_args.csr: @@ -303,16 +340,12 @@ class HelpfulArgumentParser(object): "cannot be used with --csr") self.handle_csr(parsed_args) - if self.detect_defaults: # plumbing - parsed_args.store_false_vars = self.store_false_vars + hooks.validate_hooks(parsed_args) return parsed_args def handle_csr(self, parsed_args): - """ - Process a --csr flag. This needs to happen early enough that the - webroot plugin can know about the calls to process_domain - """ + """Process a --csr flag.""" if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " "when obtaining a new or replacement " @@ -333,14 +366,11 @@ class HelpfulArgumentParser(object): logger.debug("DER CSR parse error %s", e1) logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0])) - for d in domains: - process_domain(parsed_args, d) - for d in domains: - sanitised = le_util.enforce_domain_sanity(d) - if d.lower() != sanitised: - raise errors.ConfigurationError( - "CSR domain {0} needs to be sanitised to {1}.".format(d, sanitised)) + # This is not necessary for webroot to work, however, + # obtain_certificate_from_csr requires parsed_args.domains to be set + for domain in domains: + add_domains(parsed_args, domain) if not domains: # TODO: add CN to domains instead: @@ -412,7 +442,7 @@ class HelpfulArgumentParser(object): """ if self.detect_defaults: - kwargs = self.modify_arg_for_default_detection(self, *args, **kwargs) + kwargs = self.modify_kwargs_for_default_detection(**kwargs) if self.visible_topics[topic]: if topic in self.groups: @@ -424,39 +454,28 @@ class HelpfulArgumentParser(object): kwargs["help"] = argparse.SUPPRESS self.parser.add_argument(*args, **kwargs) + def modify_kwargs_for_default_detection(self, **kwargs): + """Modify an arg so we can check if it was set by the user. - def modify_arg_for_default_detection(self, *args, **kwargs): - """ - Adding an arg, but ensure that it has a default that evaluates to false, - so that set_by_cli can tell if it was set. Only called if detect_defaults==True. + Changes the parameters given to argparse when adding an argument + so we can properly detect if the value was set by the user. - :param list *args: the names of this argument flag - :param dict **kwargs: various argparse settings for this argument + :param dict kwargs: various argparse settings for this argument :returns: a modified versions of kwargs + :rtype: dict + """ - # argument either doesn't have a default, or the default doesn't - # isn't Pythonically false - if kwargs.get("default", True): - arg_type = kwargs.get("type", None) - if arg_type == int or kwargs.get("action", "") == "count": - kwargs["default"] = 0 - elif arg_type == read_file or "-c" in args: - kwargs["default"] = "" - kwargs["type"] = str - else: - kwargs["default"] = "" - # This doesn't matter at present (none of the store_false args - # are renewal-relevant), but implement it for future sanity: - # detect the setting of args whose presence causes True -> False - if kwargs.get("action", "") == "store_false": - kwargs["default"] = None - for var in args: - self.store_false_vars[var] = True + action = kwargs.get("action", None) + if action not in EXIT_ACTIONS: + kwargs["action"] = ("store_true" if action in ZERO_ARG_ACTIONS else + "store") + kwargs["default"] = _Default() + for param in ARGPARSE_PARAMS_TO_REMOVE: + kwargs.pop(param, None) return kwargs - def add_deprecated_argument(self, argument_name, num_args): """Adds a deprecated argument with the name argument_name. @@ -472,22 +491,22 @@ class HelpfulArgumentParser(object): self.parser.add_argument, argument_name, num_args) def add_group(self, topic, **kwargs): - """ + """Create a new argument group. - This has to be called once for every topic; but we leave those calls - next to the argument definitions for clarity. Return something - arguments can be added to if necessary, either the parser or an argument - group. + This method must be called once for every topic, however, calls + to this function are left next to the argument definitions for + clarity. + + :param str topic: Name of the new argument group. + + :returns: The new argument group. + :rtype: `HelpfulArgumentGroup` """ if self.visible_topics[topic]: - #print("Adding visible group " + topic) - group = self.parser.add_argument_group(topic, **kwargs) - self.groups[topic] = group - return group - else: - #print("Invisible group " + topic) - return self.silent_parser + self.groups[topic] = self.parser.add_argument_group(topic, **kwargs) + + return HelpfulArgumentGroup(self, topic) def add_plugin_args(self, plugins): """ @@ -498,7 +517,6 @@ class HelpfulArgumentParser(object): """ for name, plugin_ep in six.iteritems(plugins): parser_or_group = self.add_group(name, description=plugin_ep.description) - #print(parser_or_group) plugin_ep.plugin_cls.inject_parser_options(parser_or_group, name) def determine_help_topics(self, chosen_topic): @@ -555,7 +573,14 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): None, "--dry-run", action="store_true", dest="dry_run", help="Perform a test run of the client, obtaining test (invalid) certs" " but not saving them to disk. This can currently only be used" - " with the 'certonly' subcommand.") + " with the 'certonly' and 'renew' subcommands. \nNote: Although --dry-run" + " tries to avoid making any persistent changes on a system, it " + " is not completely side-effect free: if used with webserver authenticator plugins" + " like apache and nginx, it makes and then reverts temporary config changes" + " in order to obtain test certs, and reloads webservers to deploy and then" + " roll back those changes. It also calls --pre-hook and --post-hook commands" + " if they are defined because they may be necessary to accurately simulate" + " renewal. --renew-hook commands are not called.") helpful.add( None, "--register-unsafely-without-email", action="store_true", help="Specifying this flag enables registering an account with no " @@ -572,7 +597,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): #for subparser in parser_run, parser_auth, parser_install: # subparser.add_argument("domains", nargs="*", metavar="domain") helpful.add(None, "-d", "--domains", "--domain", dest="domains", - metavar="DOMAIN", action=DomainFlagProcessor, default=[], + metavar="DOMAIN", action=_DomainsAction, default=[], help="Domain names to apply. For multiple domains you can use " "multiple -d flags or enter a comma separated list of domains " "as a parameter.") @@ -591,7 +616,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "always expand and replace it with the additional names.") helpful.add( "automation", "--version", action="version", - version="%(prog)s {0}".format(letsencrypt.__version__), + version="%(prog)s {0}".format(certbot.__version__), help="show program's version number and exit") helpful.add( "automation", "--force-renewal", "--renew-by-default", @@ -600,9 +625,16 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "regardless of whether it is near expiry. (Often " "--keep-until-expiring is more appropriate). Also implies " "--expand.") + helpful.add( + "automation", "--allow-subset-of-names", action="store_true", + help="When performing domain validation, do not consider it a failure " + "if authorizations can not be obtained for a strict subset of " + "the requested domains. This may be useful for allowing renewals for " + "multiple domains to succeed even if some domains no longer point " + "at this system. This option cannot be used with --csr.") helpful.add( "automation", "--agree-tos", dest="tos", action="store_true", - help="Agree to the Let's Encrypt Subscriber Agreement") + help="Agree to the ACME Subscriber Agreement") helpful.add( "automation", "--account", metavar="ACCOUNT_ID", help="Account ID to use") @@ -617,6 +649,10 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "automation", "--no-self-upgrade", action="store_true", help="(letsencrypt-auto only) prevent the letsencrypt-auto script from" " upgrading itself to newer released versions") + helpful.add( + "automation", "-q", "--quiet", dest="quiet", action="store_true", + help="Silence all output except errors. Useful for automation via cron." + " Implies --non-interactive.") helpful.add_group( "testing", description="The following flags are meant for " @@ -677,12 +713,6 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "security", "--strict-permissions", action="store_true", help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") - helpful.add( - "automation", "--allow-subset-of-names", - action="store_true", - help="When performing domain validation, do not consider it a failure " - "if authorizations can not be obtained for a strict subset of " - "the requested domains. This option cannot be used with --csr.") helpful.add_group( "renew", description="The 'renew' subcommand will attempt to renew all" @@ -692,7 +722,26 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " used to create obtain or most recently successfully renew each" " certificate lineage. You can try it with `--dry-run` first. For" " more fine-grained control, you can renew individual lineages with" - " the `certonly` subcommand.") + " the `certonly` subcommand. Hooks are available to run commands " + " before and after renewal; see XXX for more information on these.") + + helpful.add( + "renew", "--pre-hook", + help="Command to be run in a shell before obtaining any certificates. Intended" + " primarily for renewal, where it can be used to temporarily shut down a" + " webserver that might conflict with the standalone plugin. This will " + " only be called if a certificate is actually to be obtained/renewed. ") + helpful.add( + "renew", "--post-hook", + help="Command to be run in a shell after attempting to obtain/renew " + " certificates. Can be used to deploy renewed certificates, or to restart" + " any servers that were stopped by --pre-hook.") + helpful.add( + "renew", "--renew-hook", + help="Command to be run in a shell once for each successfully renewed certificate." + "For this command, the shell variable $RENEWED_LINEAGE will point to the" + "config live subdirectory containing the new certs and keys; the shell variable " + "$RENEWED_DOMAINS will contain a space-delimited list of renewed cert domains") helpful.add_deprecated_argument("--agree-dev-preview", 0) @@ -799,7 +848,7 @@ def _paths_parser(helpful): def _plugins_parsing(helpful, plugins): helpful.add_group( - "plugins", description="Let's Encrypt client supports an " + "plugins", description="Certbot client supports an " "extensible plugins architecture. See '%(prog)s plugins' for a " "list of all installed plugins and their names. You can force " "a particular plugin by setting options provided below. Running " @@ -829,83 +878,35 @@ def _plugins_parsing(helpful, plugins): helpful.add_plugin_args(plugins) - # These would normally be a flag within the webroot plugin, but because - # they are parsed in conjunction with --domains, they live here for - # legibility. helpful.add_plugin_ags must be called first to add the - # "webroot" topic - helpful.add("webroot", "-w", "--webroot-path", default=[], action=WebrootPathProcessor, - help="public_html / webroot path. This can be specified multiple times to " - "handle different domains; each domain will have the webroot path that" - " preceded it. For instance: `-w /var/www/example -d example.com -d " - "www.example.com -w /var/www/thing -d thing.net -d m.thing.net`") - # --webroot-map still has some awkward properties, so it is undocumented - helpful.add("webroot", "--webroot-map", default={}, action=WebrootMapProcessor, - help="JSON dictionary mapping domains to webroot paths; this " - "implies -d for each entry. You may need to escape this " - "from your shell. E.g.: --webroot-map " - """'{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}' """ - "This option is merged with, but takes precedence over, " - "-w / -d entries. At present, if you put webroot-map in " - "a config file, it needs to be on a single line, like: " - 'webroot-map = {"example.com":"/var/www"}.') + +class _DomainsAction(argparse.Action): + """Action class for parsing domains.""" + + def __call__(self, parser, namespace, domain, option_string=None): + """Just wrap add_domains in argparseese.""" + add_domains(namespace, domain) -class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstring - def __init__(self, *args, **kwargs): - self.domain_before_webroot = False - argparse.Action.__init__(self, *args, **kwargs) +def add_domains(args_or_config, domains): + """Registers new domains to be used during the current client run. - def __call__(self, parser, args, webroot, option_string=None): - """ - Keep a record of --webroot-path / -w flags during processing, so that - we know which apply to which -d flags - """ - if not args.webroot_path: # first -w flag encountered - # if any --domain flags preceded the first --webroot-path flag, - # apply that webroot path to those; subsequent entries in - # args.webroot_map are filled in by cli.DomainFlagProcessor - if args.domains: - self.domain_before_webroot = True - for d in args.domains: - args.webroot_map.setdefault(d, webroot) - elif self.domain_before_webroot: - # FIXME if you set domains in a args file, you should get a different error - # here, pointing you to --webroot-map - raise errors.Error("If you specify multiple webroot paths, one of " - "them must precede all domain flags") - args.webroot_path.append(webroot) + Domains are not added to the list of requested domains if they have + already been registered. + :param args_or_config: parsed command line arguments + :type args_or_config: argparse.Namespace or + configuration.NamespaceConfig + :param str domain: one or more comma separated domains -def process_domain(args_or_config, domain_arg, webroot_path=None): - """ - Process a new -d flag, helping the webroot plugin construct a map of - {domain : webrootpath} if -w / --webroot-path is in use - - :param args_or_config: may be an argparse args object, or a NamespaceConfig object - :param str domain_arg: a string representing 1+ domains, eg: "eg.is, example.com" - :param str webroot_path: (optional) the webroot_path for these domains + :returns: domains after they have been normalized and validated + :rtype: `list` of `str` """ - webroot_path = webroot_path if webroot_path else args_or_config.webroot_path - - for domain in (d.strip() for d in domain_arg.split(",")): - domain = le_util.enforce_domain_sanity(domain) + validated_domains = [] + for domain in domains.split(","): + domain = le_util.enforce_domain_sanity(domain.strip()) + validated_domains.append(domain) if domain not in args_or_config.domains: args_or_config.domains.append(domain) - # Each domain has a webroot_path of the most recent -w flag - # unless it was explicitly included in webroot_map - if webroot_path: - args_or_config.webroot_map.setdefault(domain, webroot_path[-1]) - -class WebrootMapProcessor(argparse.Action): # pylint: disable=missing-docstring - def __call__(self, parser, args, webroot_map_arg, option_string=None): - webroot_map = json.loads(webroot_map_arg) - for domains, webroot_path in six.iteritems(webroot_map): - process_domain(args, domains, [webroot_path]) - - -class DomainFlagProcessor(argparse.Action): # pylint: disable=missing-docstring - def __call__(self, parser, args, domain_arg, option_string=None): - """Just wrap process_domain in argparseese.""" - process_domain(args, domain_arg) + return validated_domains diff --git a/letsencrypt/client.py b/certbot/client.py similarity index 94% rename from letsencrypt/client.py rename to certbot/client.py index da2e1f086..60e37a787 100644 --- a/letsencrypt/client.py +++ b/certbot/client.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client API.""" +"""Certbot client API.""" import logging import os @@ -11,23 +11,23 @@ from acme import client as acme_client from acme import jose from acme import messages -import letsencrypt +import certbot -from letsencrypt import account -from letsencrypt import auth_handler -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import reverter -from letsencrypt import storage +from certbot import account +from certbot import auth_handler +from certbot import configuration +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import error_handler +from certbot import interfaces +from certbot import le_util +from certbot import reverter +from certbot import storage -from letsencrypt.display import ops as display_ops -from letsencrypt.display import enhancements -from letsencrypt.plugins import selection as plugin_selection +from certbot.display import ops as display_ops +from certbot.display import enhancements +from certbot.plugins import selection as plugin_selection logger = logging.getLogger(__name__) @@ -51,8 +51,8 @@ def _determine_user_agent(config): """ if config.user_agent is None: - ua = "LetsEncryptPythonClient/{0} ({1}) Authenticator/{2} Installer/{3}" - ua = ua.format(letsencrypt.__version__, " ".join(le_util.get_os_info()), + ua = "CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3}" + ua = ua.format(certbot.__version__, " ".join(le_util.get_os_info()), config.authenticator, config.installer) else: ua = config.user_agent @@ -87,7 +87,7 @@ def register(config, account_storage, tos_cb=None): None``. This argument is optional, if not supplied it will default to automatic acceptance! - :raises letsencrypt.errors.Error: In case of any client problems, in + :raises certbot.errors.Error: In case of any client problems, in particular registration failure, or unaccepted Terms of Service. :raises acme.errors.Error: In case of any protocol problems. @@ -105,7 +105,8 @@ def register(config, account_storage, tos_cb=None): "--register-unsafely-without-email was not present.") logger.warn(msg) raise errors.Error(msg) - logger.warn("Registering without email!") + if not config.dry_run: + logger.warn("Registering without email!") # Each new registration shall use a fresh new key key = jose.JWKRSA(key=jose.ComparableRSAKey( @@ -265,7 +266,7 @@ class Client(object): :param list domains: Domains to request. :param plugins: A PluginsFactory object. - :returns: A new :class:`letsencrypt.storage.RenewableCert` instance + :returns: A new :class:`certbot.storage.RenewableCert` instance referred to the enrolled cert lineage, False if the cert could not be obtained, or None if doing a successful dry run. @@ -359,7 +360,7 @@ class Client(object): fullchain_path=fullchain_path) self.installer.save() # needed by the Apache plugin - self.installer.save("Deployed Let's Encrypt Certificate") + self.installer.save("Deployed ACME Certificate") msg = ("We were unable to install your certificate, " "however, we successfully restored your " @@ -491,7 +492,7 @@ def validate_key_csr(privkey, csr=None): If csr is left as None, only the key will be validated. :param privkey: Key associated with CSR - :type privkey: :class:`letsencrypt.le_util.Key` + :type privkey: :class:`certbot.le_util.Key` :param .le_util.CSR csr: CSR @@ -531,7 +532,7 @@ def rollback(default_installer, checkpoints, config, plugins): :param int checkpoints: Number of checkpoints to revert. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ # Misconfigurations are only a slight problems... allow the user to rollback @@ -553,7 +554,7 @@ def view_config_changes(config, num=None): .. note:: This assumes that the installation is using a Reverter object. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ rev = reverter.Reverter(config) diff --git a/letsencrypt/colored_logging.py b/certbot/colored_logging.py similarity index 97% rename from letsencrypt/colored_logging.py rename to certbot/colored_logging.py index 443364ddd..d42fb5966 100644 --- a/letsencrypt/colored_logging.py +++ b/certbot/colored_logging.py @@ -2,7 +2,7 @@ import logging import sys -from letsencrypt import le_util +from certbot import le_util class StreamHandler(logging.StreamHandler): diff --git a/letsencrypt/configuration.py b/certbot/configuration.py similarity index 90% rename from letsencrypt/configuration.py rename to certbot/configuration.py index 062722346..172b35bfe 100644 --- a/letsencrypt/configuration.py +++ b/certbot/configuration.py @@ -1,14 +1,14 @@ -"""Let's Encrypt user-supplied configuration.""" +"""Certbot user-supplied configuration.""" import copy import os from six.moves.urllib import parse # pylint: disable=import-error import zope.interface -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import errors +from certbot import interfaces +from certbot import le_util @zope.interface.implementer(interfaces.IConfig) @@ -16,10 +16,10 @@ class NamespaceConfig(object): """Configuration wrapper around :class:`argparse.Namespace`. For more documentation, including available attributes, please see - :class:`letsencrypt.interfaces.IConfig`. However, note that + :class:`certbot.interfaces.IConfig`. However, note that the following attributes are dynamically resolved using - :attr:`~letsencrypt.interfaces.IConfig.work_dir` and relative - paths defined in :py:mod:`letsencrypt.constants`: + :attr:`~certbot.interfaces.IConfig.work_dir` and relative + paths defined in :py:mod:`certbot.constants`: - `accounts_dir` - `csr_dir` @@ -119,7 +119,7 @@ def check_config_sanity(config): requirements are not met. :param config: IConfig instance holding user configuration - :type args: :class:`letsencrypt.interfaces.IConfig` + :type args: :class:`certbot.interfaces.IConfig` """ # Port check diff --git a/letsencrypt/constants.py b/certbot/constants.py similarity index 90% rename from letsencrypt/constants.py rename to certbot/constants.py index f8ef1e845..1d4efe80e 100644 --- a/letsencrypt/constants.py +++ b/certbot/constants.py @@ -1,13 +1,16 @@ -"""Let's Encrypt constants.""" +"""Certbot constants.""" import os import logging from acme import challenges -SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins" +SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins" """Setuptools entry point group name for plugins.""" +OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins" +"""Plugins Setuptools entry point before rename.""" + CLI_DEFAULTS = dict( config_files=[ "/etc/letsencrypt/cli.ini", @@ -45,7 +48,7 @@ RENEWER_DEFAULTS = dict( ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"] -"""List of possible :class:`letsencrypt.interfaces.IInstaller` +"""List of possible :class:`certbot.interfaces.IInstaller` enhancements. List of expected options parameters: diff --git a/letsencrypt/crypto_util.py b/certbot/crypto_util.py similarity index 95% rename from letsencrypt/crypto_util.py rename to certbot/crypto_util.py index 5fdcba843..b699ce653 100644 --- a/letsencrypt/crypto_util.py +++ b/certbot/crypto_util.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client crypto utility functions. +"""Certbot client crypto utility functions. .. todo:: Make the transition to use PSS rather than PKCS1_v1_5 when the server is capable of handling the signatures. @@ -14,16 +14,16 @@ import zope.component from acme import crypto_util as acme_crypto_util from acme import jose -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) # High level functions -def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): +def init_save_key(key_size, key_dir, keyname="key-certbot.pem"): """Initializes and saves a privkey. Inits key and saves it in PEM format on the filesystem. @@ -36,7 +36,7 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): :param str keyname: Filename of key :returns: Key - :rtype: :class:`letsencrypt.le_util.Key` + :rtype: :class:`certbot.le_util.Key` :raises ValueError: If unable to generate the key given key_size. @@ -61,18 +61,18 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): return le_util.Key(key_path, key_pem) -def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"): +def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"): """Initialize a CSR with the given private key. :param privkey: Key to include in the CSR - :type privkey: :class:`letsencrypt.le_util.Key` + :type privkey: :class:`certbot.le_util.Key` :param set names: `str` names to include in the CSR :param str path: Certificate save directory. :returns: CSR - :rtype: :class:`letsencrypt.le_util.CSR` + :rtype: :class:`certbot.le_util.CSR` """ csr_pem, csr_der = make_csr(privkey.pem, names) diff --git a/certbot/display/__init__.py b/certbot/display/__init__.py new file mode 100644 index 000000000..9d39dce92 --- /dev/null +++ b/certbot/display/__init__.py @@ -0,0 +1 @@ +"""Certbot display utilities.""" diff --git a/letsencrypt/display/completer.py b/certbot/display/completer.py similarity index 97% rename from letsencrypt/display/completer.py rename to certbot/display/completer.py index fed476bb3..37564954a 100644 --- a/letsencrypt/display/completer.py +++ b/certbot/display/completer.py @@ -4,7 +4,7 @@ import glob try: import readline except ImportError: - import letsencrypt.display.dummy_readline as readline + import certbot.display.dummy_readline as readline class Completer(object): diff --git a/letsencrypt/display/dummy_readline.py b/certbot/display/dummy_readline.py similarity index 100% rename from letsencrypt/display/dummy_readline.py rename to certbot/display/dummy_readline.py diff --git a/letsencrypt/display/enhancements.py b/certbot/display/enhancements.py similarity index 86% rename from letsencrypt/display/enhancements.py rename to certbot/display/enhancements.py index 39def1651..3b128a874 100644 --- a/letsencrypt/display/enhancements.py +++ b/certbot/display/enhancements.py @@ -1,11 +1,11 @@ -"""Let's Encrypt Enhancement Display""" +"""Certbot Enhancement Display""" import logging import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.display import util as display_util +from certbot import errors +from certbot import interfaces +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -18,7 +18,7 @@ def ask(enhancement): """Display the enhancement to the user. :param str enhancement: One of the - :class:`letsencrypt.CONFIG.ENHANCEMENTS` enhancements + :class:`certbot.CONFIG.ENHANCEMENTS` enhancements :returns: True if feature is desired, False otherwise :rtype: bool diff --git a/letsencrypt/display/ops.py b/certbot/display/ops.py similarity index 96% rename from letsencrypt/display/ops.py rename to certbot/display/ops.py index 302051b1b..6752bf0c1 100644 --- a/letsencrypt/display/ops.py +++ b/certbot/display/ops.py @@ -4,10 +4,10 @@ import os import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt.display import util as display_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -56,7 +56,7 @@ def choose_account(accounts): """Choose an account. :param list accounts: Containing at least one - :class:`~letsencrypt.account.Account` + :class:`~certbot.account.Account` """ # Note this will get more complicated once we start recording authorizations @@ -74,7 +74,7 @@ def choose_names(installer): """Display screen to select domains to validate. :param installer: An installer object - :type installer: :class:`letsencrypt.interfaces.IInstaller` + :type installer: :class:`certbot.interfaces.IInstaller` :returns: List of selected names :rtype: `list` of `str` diff --git a/letsencrypt/display/util.py b/certbot/display/util.py similarity index 99% rename from letsencrypt/display/util.py rename to certbot/display/util.py index 20c6be156..8de607534 100644 --- a/letsencrypt/display/util.py +++ b/certbot/display/util.py @@ -1,13 +1,13 @@ -"""Let's Encrypt display.""" +"""Certbot display.""" import os import textwrap import dialog import zope.interface -from letsencrypt import interfaces -from letsencrypt import errors -from letsencrypt.display import completer +from certbot import interfaces +from certbot import errors +from certbot.display import completer WIDTH = 72 HEIGHT = 20 diff --git a/letsencrypt/error_handler.py b/certbot/error_handler.py similarity index 100% rename from letsencrypt/error_handler.py rename to certbot/error_handler.py diff --git a/letsencrypt/errors.py b/certbot/errors.py similarity index 84% rename from letsencrypt/errors.py rename to certbot/errors.py index b2b078f6a..1553b6317 100644 --- a/letsencrypt/errors.py +++ b/certbot/errors.py @@ -1,8 +1,8 @@ -"""Let's Encrypt client errors.""" +"""Certbot client errors.""" class Error(Exception): - """Generic Let's Encrypt client error.""" + """Generic Certbot client error.""" class AccountStorageError(Error): @@ -14,7 +14,7 @@ class AccountNotFound(AccountStorageError): class ReverterError(Error): - """Let's Encrypt Reverter error.""" + """Certbot Reverter error.""" class SubprocessError(Error): @@ -25,6 +25,10 @@ class CertStorageError(Error): """Generic `.CertStorage` error.""" +class HookCommandNotFound(Error): + """Failed to find a hook command in the PATH.""" + + # Auth Handler Errors class AuthorizationError(Error): """Authorization error.""" @@ -50,7 +54,7 @@ class FailedChallenges(AuthorizationError): # Plugin Errors class PluginError(Error): - """Let's Encrypt Plugin error.""" + """Certbot Plugin error.""" class PluginEnhancementAlreadyPresent(Error): @@ -62,15 +66,15 @@ class PluginSelectionError(Error): class NoInstallationError(PluginError): - """Let's Encrypt No Installation error.""" + """Certbot No Installation error.""" class MisconfigurationError(PluginError): - """Let's Encrypt Misconfiguration error.""" + """Certbot Misconfiguration error.""" class NotSupportedError(PluginError): - """Let's Encrypt Plugin function not supported error.""" + """Certbot Plugin function not supported error.""" class StandaloneBindError(Error): diff --git a/certbot/hooks.py b/certbot/hooks.py new file mode 100644 index 000000000..138e2addc --- /dev/null +++ b/certbot/hooks.py @@ -0,0 +1,98 @@ +"""Facilities for implementing hooks that call shell commands.""" +from __future__ import print_function + +import logging +import os + +from subprocess import Popen, PIPE + +from certbot import errors + +logger = logging.getLogger(__name__) + +def validate_hooks(config): + """Check hook commands are executable.""" + _validate_hook(config.pre_hook, "pre") + _validate_hook(config.post_hook, "post") + _validate_hook(config.renew_hook, "renew") + +def _prog(shell_cmd): + """Extract the program run by a shell command""" + cmd = _which(shell_cmd) + return os.path.basename(cmd) if cmd else None + +def _validate_hook(shell_cmd, hook_name): + """Check that a command provided as a hook is plausibly executable. + + :raises .errors.HookCommandNotFound: if the command is not found + """ + if shell_cmd: + cmd = shell_cmd.partition(" ")[0] + if not _prog(cmd): + path = os.environ["PATH"] + msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format( + cmd, path, hook_name) + raise errors.HookCommandNotFound(msg) + +def pre_hook(config): + "Run pre-hook if it's defined and hasn't been run." + if config.pre_hook and not pre_hook.already: + logger.info("Running pre-hook command: %s", config.pre_hook) + _run_hook(config.pre_hook) + pre_hook.already = True + +pre_hook.already = False + +def post_hook(config, final=False): + """Run post hook if defined. + + If the verb is renew, we might have more certs to renew, so we wait until + we're called with final=True before actually doing anything. + """ + if config.post_hook: + if final or config.verb != "renew": + logger.info("Running post-hook command: %s", config.post_hook) + _run_hook(config.post_hook) + +def renew_hook(config, domains, lineage_path): + "Run post-renewal hook if defined." + if config.renew_hook: + if not config.dry_run: + os.environ["RENEWED_DOMAINS"] = " ".join(domains) + os.environ["RENEWED_LINEAGE"] = lineage_path + _run_hook(config.renew_hook) + else: + logger.warning("Dry run: skipping renewal hook command: %s", config.renew_hook) + +def _run_hook(shell_cmd): + """Run a hook command. + + :returns: stderr if there was any""" + + cmd = Popen(shell_cmd, shell=True, stdout=PIPE, stderr=PIPE, stdin=PIPE) + _out, err = cmd.communicate() + if cmd.returncode != 0: + logger.error('Hook command "%s" returned error code %d', shell_cmd, cmd.returncode) + if err: + logger.error('Error output from %s:\n%s', _prog(shell_cmd), err) + +def _is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def _which(program): + """Test if program is in the path.""" + # Borrowed from: + # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python + # XXX May need more porting to handle .exe extensions on Windows + + fpath, _fname = os.path.split(program) + if fpath: + if _is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if _is_exe(exe_file): + return exe_file + + return None diff --git a/letsencrypt/interfaces.py b/certbot/interfaces.py similarity index 96% rename from letsencrypt/interfaces.py rename to certbot/interfaces.py index 2fba11869..d65f5cf01 100644 --- a/letsencrypt/interfaces.py +++ b/certbot/interfaces.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client interfaces.""" +"""Certbot client interfaces.""" import abc import zope.interface @@ -51,7 +51,7 @@ class IPluginFactory(zope.interface.Interface): setup( ... entry_points={ - 'letsencrypt.plugins': [ + 'certbot.plugins': [ 'name=example_project.plugin[plugin_deps]', ], }, @@ -97,7 +97,7 @@ class IPluginFactory(zope.interface.Interface): class IPlugin(zope.interface.Interface): - """Let's Encrypt plugin.""" + """Certbot plugin.""" def prepare(): """Prepare the plugin. @@ -130,7 +130,7 @@ class IPlugin(zope.interface.Interface): class IAuthenticator(IPlugin): - """Generic Let's Encrypt Authenticator. + """Generic Certbot Authenticator. Class represents all possible tools processes that have the ability to perform challenges and attain a certificate. @@ -154,7 +154,7 @@ class IAuthenticator(IPlugin): """Perform the given challenge. :param list achalls: Non-empty (guaranteed) list of - :class:`~letsencrypt.achallenges.AnnotatedChallenge` + :class:`~certbot.achallenges.AnnotatedChallenge` instances, such that it contains types found within :func:`get_chall_pref` only. @@ -181,7 +181,7 @@ class IAuthenticator(IPlugin): """Revert changes and shutdown after challenges complete. :param list achalls: Non-empty (guaranteed) list of - :class:`~letsencrypt.achallenges.AnnotatedChallenge` + :class:`~certbot.achallenges.AnnotatedChallenge` instances, a subset of those previously passed to :func:`perform`. :raises PluginError: if original configuration cannot be restored @@ -190,7 +190,7 @@ class IAuthenticator(IPlugin): class IConfig(zope.interface.Interface): - """Let's Encrypt user-supplied configuration. + """Certbot user-supplied configuration. .. warning:: The values stored in the configuration have not been filtered, stripped or sanitized. @@ -230,7 +230,7 @@ class IConfig(zope.interface.Interface): class IInstaller(IPlugin): - """Generic Let's Encrypt Installer Interface. + """Generic Certbot Installer Interface. Represents any server that an X509 certificate can be placed. @@ -262,10 +262,10 @@ class IInstaller(IPlugin): :param str domain: domain for which to provide enhancement :param str enhancement: An enhancement as defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: Flexible options parameter for enhancement. Check documentation of - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` for expected options for each enhancement. :raises .PluginError: If Enhancement is not supported, or if @@ -277,7 +277,7 @@ class IInstaller(IPlugin): """Returns a list of supported enhancements. :returns: supported enhancements which should be a subset of - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :rtype: :class:`list` of :class:`str` """ diff --git a/letsencrypt/le_util.py b/certbot/le_util.py similarity index 99% rename from letsencrypt/le_util.py rename to certbot/le_util.py index cb1c61074..f5148b949 100644 --- a/letsencrypt/le_util.py +++ b/certbot/le_util.py @@ -1,4 +1,4 @@ -"""Utilities for all Let's Encrypt.""" +"""Utilities for all Certbot.""" import argparse import collections import errno @@ -14,7 +14,7 @@ import sys import configargparse -from letsencrypt import errors +from certbot import errors logger = logging.getLogger(__name__) diff --git a/letsencrypt/log.py b/certbot/log.py similarity index 97% rename from letsencrypt/log.py rename to certbot/log.py index 6436f6fc2..62241254a 100644 --- a/letsencrypt/log.py +++ b/certbot/log.py @@ -3,7 +3,7 @@ import logging import dialog -from letsencrypt.display import util as display_util +from certbot.display import util as display_util class DialogHandler(logging.Handler): # pylint: disable=too-few-public-methods diff --git a/letsencrypt/main.py b/certbot/main.py similarity index 81% rename from letsencrypt/main.py rename to certbot/main.py index 0afccc85e..72f4fe66e 100644 --- a/letsencrypt/main.py +++ b/certbot/main.py @@ -1,4 +1,4 @@ -"""Let's Encrypt main entry point.""" +"""Certbot main entry point.""" from __future__ import print_function import atexit import functools @@ -8,72 +8,49 @@ import sys import time import traceback -import OpenSSL import zope.component from acme import jose -import letsencrypt +import certbot -from letsencrypt import account -from letsencrypt import client -from letsencrypt import cli -from letsencrypt import crypto_util -from letsencrypt import colored_logging -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import log -from letsencrypt import reporter -from letsencrypt import renewal -from letsencrypt import storage - -from letsencrypt.display import util as display_util, ops as display_ops -from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugins import selection as plug_sel +from certbot import account +from certbot import client +from certbot import cli +from certbot import crypto_util +from certbot import colored_logging +from certbot import configuration +from certbot import constants +from certbot import errors +from certbot import hooks +from certbot import interfaces +from certbot import le_util +from certbot import log +from certbot import reporter +from certbot import renewal +from certbot import storage +from certbot.display import util as display_util, ops as display_ops +from certbot.plugins import disco as plugins_disco +from certbot.plugins import selection as plug_sel logger = logging.getLogger(__name__) def _suggest_donation_if_appropriate(config, action): - """Potentially suggest a donation to support Let's Encrypt.""" + """Potentially suggest a donation to support Certbot.""" if config.staging or config.verb == "renew": # --dry-run implies --staging return if action not in ["renew", "newcert"]: return reporter_util = zope.component.getUtility(interfaces.IReporter) - msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" + msg = ("If you like Certbot, please consider supporting our work by:\n\n" "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" "Donating to EFF: https://eff.org/donate-le\n\n") reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) -def _avoid_invalidating_lineage(config, lineage, original_server): - "Do not renew a valid cert with one from a staging server!" - def _is_staging(srv): - return srv == constants.STAGING_URI or "staging" in srv - - # Some lineages may have begun with --staging, but then had production certs - # added to them - latest_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, - open(lineage.cert).read()) - # all our test certs are from happy hacker fake CA, though maybe one day - # we should test more methodically - now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() - - if _is_staging(config.server): - if not _is_staging(original_server) or now_valid: - if not config.break_my_certs: - names = ", ".join(lineage.names()) - raise errors.Error( - "You've asked to renew/replace a seemingly valid certificate with " - "a test certificate (domains: {0}). We will not do that " - "unless you use the --break-my-certs flag!".format(names)) - def _report_successful_dry_run(config): reporter_util = zope.component.getUtility(interfaces.IReporter) @@ -82,6 +59,7 @@ def _report_successful_dry_run(config): reporter_util.HIGH_PRIORITY, on_crash=False) + def _auth_from_domains(le_client, config, domains, lineage=None): """Authenticate and enroll certificate.""" # Note: This can raise errors... caught above us though. This is now @@ -105,31 +83,18 @@ def _auth_from_domains(le_client, config, domains, lineage=None): # The lineage already exists; allow the caller to try installing # it without getting a new certificate at all. return lineage, "reinstall" - elif action == "renew": - original_server = lineage.configuration["renewalparams"]["server"] - _avoid_invalidating_lineage(config, lineage, original_server) - # TODO: schoen wishes to reuse key - discussion - # https://github.com/letsencrypt/letsencrypt/pull/777/files#r40498574 - new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) - # TODO: Check whether it worked! <- or make sure errors are thrown (jdk) - if config.dry_run: - logger.info("Dry run: skipping updating lineage at %s", - os.path.dirname(lineage.cert)) - else: - lineage.save_successor( - lineage.latest_common_version(), OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped), - new_key.pem, crypto_util.dump_pyopenssl_chain(new_chain), - configuration.RenewerConfiguration(config.namespace)) - lineage.update_all_links_to(lineage.latest_common_version()) - # TODO: Check return value of save_successor - # TODO: Also update lineage renewal config with any relevant - # configuration values from this attempt? <- Absolutely (jdkasten) - elif action == "newcert": - # TREAT AS NEW REQUEST - lineage = le_client.obtain_and_enroll_certificate(domains) - if lineage is False: - raise errors.Error("Certificate could not be obtained") + + hooks.pre_hook(config) + try: + if action == "renew": + renewal.renew_cert(config, domains, le_client, lineage) + elif action == "newcert": + # TREAT AS NEW REQUEST + lineage = le_client.obtain_and_enroll_certificate(domains) + if lineage is False: + raise errors.Error("Certificate could not be obtained") + finally: + hooks.post_hook(config) if not config.dry_run and not config.verb == "renew": _report_new_cert(lineage.cert, lineage.fullchain) @@ -142,7 +107,8 @@ def _handle_subset_cert_request(config, domains, cert): :param storage.RenewableCert cert: - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (str action, cert_or_None) as per _treat_as_renewal + action can be: "newcert" | "renew" | "reinstall" :rtype: tuple """ @@ -183,7 +149,8 @@ def _handle_identical_cert_request(config, cert): :param storage.RenewableCert cert: - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (str action, cert_or_None) as per _treat_as_renewal + action can be: "newcert" | "renew" | "reinstall" :rtype: tuple """ @@ -288,14 +255,10 @@ def _find_duplicative_certs(config, domains): def _find_domains(config, installer): - if not config.domains: - domains = display_ops.choose_names(installer) - # record in config.domains (so that it can be serialised in renewal config files), - # and set webroot_map entries if applicable - for d in domains: - cli.process_domain(config, d) - else: + if config.domains: domains = config.domains + else: + domains = display_ops.choose_names(installer) if not domains: raise errors.Error("Please specify --domains, or --installer that " @@ -326,7 +289,7 @@ def _report_new_cert(cert_path, fullchain_path): # and say something more informative here. msg = ("Congratulations! Your certificate {0} been saved at {1}." " Your cert will expire on {2}. To obtain a new version of the " - "certificate in the future, simply run Let's Encrypt again." + "certificate in the future, simply run Certbot again." .format(and_chain, path, expiry)) reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) @@ -339,12 +302,12 @@ def _determine_account(config): user input. Same for ``config.email``. :param argparse.Namespace config: CLI arguments - :param letsencrypt.interface.IConfig config: Configuration object + :param certbot.interface.IConfig config: Configuration object :param .AccountStorage account_storage: Account storage. :returns: Account and optionally ACME client API (biproduct of new registration). - :rtype: `tuple` of `letsencrypt.account.Account` and + :rtype: `tuple` of `certbot.account.Account` and `acme.client.Client` """ @@ -507,56 +470,74 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals _suggest_donation_if_appropriate(config, action) +def _csr_obtain_cert(config, le_client): + """Obtain a cert using a user-supplied CSR + + This works differently in the CSR case (for now) because we don't + have the privkey, and therefore can't construct the files for a lineage. + So we just save the cert & chain to disk :/ + """ + csr, typ = config.actual_csr + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) + if config.dry_run: + logger.info( + "Dry run: skipping saving certificate to %s", config.cert_path) + else: + cert_path, _, cert_fullchain = le_client.save_certificate( + certr, chain, config.cert_path, config.chain_path, config.fullchain_path) + _report_new_cert(cert_path, cert_fullchain) + + def obtain_cert(config, plugins, lineage=None): - """Implements "certonly": authenticate & obtain cert, but do not install it.""" - # pylint: disable=too-many-locals + """Authenticate & obtain cert, but do not install it. + + This implements the 'certonly' subcommand, and is also called from within the + 'renew' command.""" + + # SETUP: Select plugins and construct a client instance try: # installers are used in auth mode to determine domain names - installer, authenticator = plug_sel.choose_configurator_plugins(config, plugins, "certonly") + installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise + le_client = _init_le_client(config, auth, installer) - # TODO: Handle errors from _init_le_client? - le_client = _init_le_client(config, authenticator, installer) - - action = "newcert" - # This is a special case; cert and chain are simply saved - if config.csr is not None: - assert lineage is None, "Did not expect a CSR with a RenewableCert" - csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) - if config.dry_run: - logger.info( - "Dry run: skipping saving certificate to %s", config.cert_path) - else: - cert_path, _, cert_fullchain = le_client.save_certificate( - certr, chain, config.cert_path, config.chain_path, config.fullchain_path) - _report_new_cert(cert_path, cert_fullchain) - else: + # SHOWTIME: Possibly obtain/renew a cert, and set action to renew | newcert | reinstall + if config.csr is None: # the common case domains = _find_domains(config, installer) _, action = _auth_from_domains(le_client, config, domains, lineage) + else: + assert lineage is None, "Did not expect a CSR with a RenewableCert" + _csr_obtain_cert(config, le_client) + action = "newcert" + # POSTPRODUCTION: Cleanup, deployment & reporting + notify = zope.component.getUtility(interfaces.IDisplay).notification if config.dry_run: _report_successful_dry_run(config) elif config.verb == "renew": if installer is None: - # Tell the user that the server was not restarted. - print("new certificate deployed without reload, fullchain is", - lineage.fullchain) + notify("new certificate deployed without reload, fullchain is {0}".format( + lineage.fullchain), pause=False) else: # In case of a renewal, reload server to pick up new certificate. # In principle we could have a configuration option to inhibit this # from happening. installer.restart() - print("new certificate deployed with reload of", - config.installer, "server; fullchain is", lineage.fullchain) + notify("new certificate deployed with reload of {0} server; fullchain is {1}".format( + config.installer, lineage.fullchain), pause=False) + elif action == "reinstall" and config.verb == "certonly": + notify("Certificate not yet due for renewal; no action taken.") _suggest_donation_if_appropriate(config, action) + def renew(config, unused_plugins): """Renew previously-obtained certificates.""" - renewal.renew_all_lineages(config) - + try: + renewal.renew_all_lineages(config) + finally: + hooks.post_hook(config, final=True) def setup_log_file_handler(config, logfile, fmt): @@ -624,7 +605,7 @@ def _handle_exception(exc_type, exc_value, trace, config): if issubclass(exc_type, Exception) and (config is None or not config.debug): if config is None: - logfile = "letsencrypt.log" + logfile = "certbot.log" try: with open(logfile, "w") as logfd: traceback.print_exception( @@ -681,7 +662,7 @@ def main(cli_args=sys.argv[1:]): config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') - logger.debug("letsencrypt version: %s", letsencrypt.__version__) + logger.debug("certbot version: %s", certbot.__version__) # do not log `config`, as it contains sensitive data (e.g. revoke --key)! logger.debug("Arguments: %r", cli_args) logger.debug("Discovered plugins: %r", plugins) @@ -689,7 +670,10 @@ def main(cli_args=sys.argv[1:]): sys.excepthook = functools.partial(_handle_exception, config=config) # Displayer - if config.noninteractive_mode: + if config.quiet: + config.noninteractive_mode = True + displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w")) + elif config.noninteractive_mode: displayer = display_util.NoninteractiveDisplay(sys.stdout) elif config.text_mode: displayer = display_util.FileDisplay(sys.stdout) @@ -701,7 +685,7 @@ def main(cli_args=sys.argv[1:]): zope.component.provideUtility(displayer) # Reporter - report = reporter.Reporter() + report = reporter.Reporter(config) zope.component.provideUtility(report) atexit.register(report.atexit_print_messages) diff --git a/letsencrypt/notify.py b/certbot/notify.py similarity index 93% rename from letsencrypt/notify.py rename to certbot/notify.py index cfbfa82b0..dda0a85af 100644 --- a/letsencrypt/notify.py +++ b/certbot/notify.py @@ -14,7 +14,7 @@ def notify(subject, whom, what): """ msg = email.message_from_string(what) - msg.add_header("From", "Let's Encrypt renewal agent ") + msg.add_header("From", "Certbot renewal agent ") msg.add_header("To", whom) msg.add_header("Subject", subject) msg = msg.as_string() diff --git a/certbot/plugins/__init__.py b/certbot/plugins/__init__.py new file mode 100644 index 000000000..7b1aca2b4 --- /dev/null +++ b/certbot/plugins/__init__.py @@ -0,0 +1 @@ +"""Certbot client.plugins.""" diff --git a/letsencrypt/plugins/common.py b/certbot/plugins/common.py similarity index 95% rename from letsencrypt/plugins/common.py rename to certbot/plugins/common.py index a9410d514..757bf19d8 100644 --- a/letsencrypt/plugins/common.py +++ b/certbot/plugins/common.py @@ -10,9 +10,9 @@ import zope.interface from acme.jose import util as jose_util -from letsencrypt import constants -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import interfaces +from certbot import le_util def option_namespace(name): @@ -45,15 +45,14 @@ class Plugin(object): def add_parser_arguments(cls, add): """Add plugin arguments to the CLI argument parser. + NOTE: If some of your flags interact with others, you can + use cli.report_config_interaction to register this to ensure + values are correctly saved/overridable during renewal. + :param callable add: Function that proxies calls to `argparse.ArgumentParser.add_argument` prepending options with unique plugin name prefix. - NOTE: if you add argpase arguments such that users setting them can - create a config entry that python's bool() would consider false (ie, - the use might set the variable to "", [], 0, etc), please ensure that - cli.set_by_cli() works for your variable. - """ @classmethod @@ -262,7 +261,7 @@ class TLSSNI01(object): return response -# test utils used by letsencrypt_apache/letsencrypt_nginx (hence +# test utils used by certbot_apache/certbot_nginx (hence # "pragma: no cover") TODO: this might quickly lead to dead code (also # c.f. #383) diff --git a/letsencrypt/plugins/common_test.py b/certbot/plugins/common_test.py similarity index 87% rename from letsencrypt/plugins/common_test.py rename to certbot/plugins/common_test.py index a4292151e..0dd1cd522 100644 --- a/letsencrypt/plugins/common_test.py +++ b/certbot/plugins/common_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.common.""" +"""Tests for certbot.plugins.common.""" import unittest import mock @@ -7,33 +7,33 @@ import OpenSSL from acme import challenges from acme import jose -from letsencrypt import achallenges +from certbot import achallenges -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util class NamespaceFunctionsTest(unittest.TestCase): - """Tests for letsencrypt.plugins.common.*_namespace functions.""" + """Tests for certbot.plugins.common.*_namespace functions.""" def test_option_namespace(self): - from letsencrypt.plugins.common import option_namespace + from certbot.plugins.common import option_namespace self.assertEqual("foo-", option_namespace("foo")) def test_dest_namespace(self): - from letsencrypt.plugins.common import dest_namespace + from certbot.plugins.common import dest_namespace self.assertEqual("foo_", dest_namespace("foo")) def test_dest_namespace_with_dashes(self): - from letsencrypt.plugins.common import dest_namespace + from certbot.plugins.common import dest_namespace self.assertEqual("foo_bar_", dest_namespace("foo-bar")) class PluginTest(unittest.TestCase): - """Test for letsencrypt.plugins.common.Plugin.""" + """Test for certbot.plugins.common.Plugin.""" def setUp(self): - from letsencrypt.plugins.common import Plugin + from certbot.plugins.common import Plugin class MockPlugin(Plugin): # pylint: disable=missing-docstring @classmethod @@ -74,10 +74,10 @@ class PluginTest(unittest.TestCase): class AddrTest(unittest.TestCase): - """Tests for letsencrypt.client.plugins.common.Addr.""" + """Tests for certbot.client.plugins.common.Addr.""" def setUp(self): - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:*") self.addr3 = Addr.fromstring("192.168.1.1:80") @@ -132,13 +132,13 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr4, self.addr4.get_addr_obj("")) self.assertNotEqual(self.addr4, self.addr5) self.assertFalse(self.addr4 == 3333) - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr self.assertEqual(self.addr4, Addr.fromstring("[fe00:0:0::1]")) self.assertEqual(self.addr4, Addr.fromstring("[fe00:0::0:0:1]")) def test_set_inclusion(self): - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:*") @@ -155,7 +155,7 @@ class AddrTest(unittest.TestCase): class TLSSNI01Test(unittest.TestCase): - """Tests for letsencrypt.plugins.common.TLSSNI01.""" + """Tests for certbot.plugins.common.TLSSNI01.""" auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) achalls = [ @@ -166,11 +166,11 @@ class TLSSNI01Test(unittest.TestCase): achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token2'), "pending"), - domain="letsencrypt.demo", account_key=auth_key), + domain="certbot.demo", account_key=auth_key), ] def setUp(self): - from letsencrypt.plugins.common import TLSSNI01 + from certbot.plugins.common import TLSSNI01 self.sni = TLSSNI01(configurator=mock.MagicMock()) def test_add_chall(self): @@ -191,9 +191,9 @@ class TLSSNI01Test(unittest.TestCase): achall.response_and_validation.return_value = ( response, (test_util.load_cert("cert.pem"), key)) - with mock.patch("letsencrypt.plugins.common.open", + with mock.patch("certbot.plugins.common.open", mock_open, create=True): - with mock.patch("letsencrypt.plugins.common.le_util.safe_open", + with mock.patch("certbot.plugins.common.le_util.safe_open", mock_safe_open): # pylint: disable=protected-access self.assertEqual(response, self.sni._setup_challenge_cert( diff --git a/letsencrypt/plugins/disco.py b/certbot/plugins/disco.py similarity index 94% rename from letsencrypt/plugins/disco.py rename to certbot/plugins/disco.py index 27d2fb541..d88b871f6 100644 --- a/letsencrypt/plugins/disco.py +++ b/certbot/plugins/disco.py @@ -1,14 +1,15 @@ """Utilities for plugins discovery and selection.""" import collections +import itertools import logging import pkg_resources import zope.interface import zope.interface.verify -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import constants +from certbot import errors +from certbot import interfaces logger = logging.getLogger(__name__) @@ -18,9 +19,9 @@ class PluginEntryPoint(object): """Plugin entry point.""" PREFIX_FREE_DISTRIBUTIONS = [ - "letsencrypt", - "letsencrypt-apache", - "letsencrypt-nginx", + "certbot", + "certbot-apache", + "certbot-nginx", ] """Distributions for which prefix will be omitted.""" @@ -164,8 +165,12 @@ class PluginsRegistry(collections.Mapping): def find_all(cls): """Find plugins using setuptools entry points.""" plugins = {} - for entry_point in pkg_resources.iter_entry_points( - constants.SETUPTOOLS_PLUGINS_ENTRY_POINT): + entry_points = itertools.chain( + pkg_resources.iter_entry_points( + constants.SETUPTOOLS_PLUGINS_ENTRY_POINT), + pkg_resources.iter_entry_points( + constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),) + for entry_point in entry_points: plugin_ep = PluginEntryPoint(entry_point) assert plugin_ep.name not in plugins, ( "PREFIX_FREE_DISTRIBUTIONS messed up") diff --git a/letsencrypt/plugins/disco_test.py b/certbot/plugins/disco_test.py similarity index 88% rename from letsencrypt/plugins/disco_test.py rename to certbot/plugins/disco_test.py index 1aeaf81c1..cef6ede8f 100644 --- a/letsencrypt/plugins/disco_test.py +++ b/certbot/plugins/disco_test.py @@ -1,23 +1,28 @@ -"""Tests for letsencrypt.plugins.disco.""" +"""Tests for certbot.plugins.disco.""" import unittest import mock import pkg_resources import zope.interface -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.plugins import standalone +from certbot.plugins import standalone +from certbot.plugins import webroot EP_SA = pkg_resources.EntryPoint( - "sa", "letsencrypt.plugins.standalone", + "sa", "certbot.plugins.standalone", attrs=("Authenticator",), - dist=mock.MagicMock(key="letsencrypt")) + dist=mock.MagicMock(key="certbot")) +EP_WR = pkg_resources.EntryPoint( + "wr", "certbot.plugins.webroot", + attrs=("Authenticator",), + dist=mock.MagicMock(key="certbot")) class PluginEntryPointTest(unittest.TestCase): - """Tests for letsencrypt.plugins.disco.PluginEntryPoint.""" + """Tests for certbot.plugins.disco.PluginEntryPoint.""" def setUp(self): self.ep1 = pkg_resources.EntryPoint( @@ -31,11 +36,11 @@ class PluginEntryPointTest(unittest.TestCase): self.ep3 = pkg_resources.EntryPoint( "ep3", "a.ep3", dist=mock.MagicMock(key="p3")) - from letsencrypt.plugins.disco import PluginEntryPoint + from certbot.plugins.disco import PluginEntryPoint self.plugin_ep = PluginEntryPoint(EP_SA) def test_entry_point_to_plugin_name(self): - from letsencrypt.plugins.disco import PluginEntryPoint + from certbot.plugins.disco import PluginEntryPoint names = { self.ep1: "p1:ep1", @@ -100,7 +105,7 @@ class PluginEntryPointTest(unittest.TestCase): self.plugin_ep._initialized = plugin = mock.MagicMock() exceptions = zope.interface.exceptions - with mock.patch("letsencrypt.plugins." + with mock.patch("certbot.plugins." "disco.zope.interface") as mock_zope: mock_zope.exceptions = exceptions @@ -164,22 +169,25 @@ class PluginEntryPointTest(unittest.TestCase): class PluginsRegistryTest(unittest.TestCase): - """Tests for letsencrypt.plugins.disco.PluginsRegistry.""" + """Tests for certbot.plugins.disco.PluginsRegistry.""" def setUp(self): - from letsencrypt.plugins.disco import PluginsRegistry + from certbot.plugins.disco import PluginsRegistry self.plugin_ep = mock.MagicMock(name="mock") self.plugin_ep.__hash__.side_effect = TypeError self.plugins = {"mock": self.plugin_ep} self.reg = PluginsRegistry(self.plugins) def test_find_all(self): - from letsencrypt.plugins.disco import PluginsRegistry - with mock.patch("letsencrypt.plugins.disco.pkg_resources") as mock_pkg: - mock_pkg.iter_entry_points.return_value = iter([EP_SA]) + from certbot.plugins.disco import PluginsRegistry + with mock.patch("certbot.plugins.disco.pkg_resources") as mock_pkg: + mock_pkg.iter_entry_points.side_effect = [iter([EP_SA]), + iter([EP_WR])] plugins = PluginsRegistry.find_all() self.assertTrue(plugins["sa"].plugin_cls is standalone.Authenticator) self.assertTrue(plugins["sa"].entry_point is EP_SA) + self.assertTrue(plugins["wr"].plugin_cls is webroot.Authenticator) + self.assertTrue(plugins["wr"].entry_point is EP_WR) def test_getitem(self): self.assertEqual(self.plugin_ep, self.reg["mock"]) diff --git a/letsencrypt/plugins/manual.py b/certbot/plugins/manual.py similarity index 96% rename from letsencrypt/plugins/manual.py rename to certbot/plugins/manual.py index 47c8ff6e4..9b722aef4 100644 --- a/letsencrypt/plugins/manual.py +++ b/certbot/plugins/manual.py @@ -15,9 +15,9 @@ import zope.interface from acme import challenges -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import errors +from certbot import interfaces +from certbot.plugins import common logger = logging.getLogger(__name__) @@ -55,13 +55,13 @@ command on the target server (as root): # a disclaimer about your current IP being transmitted to Let's Encrypt's servers. IP_DISCLAIMER = """\ NOTE: The IP of this machine will be publicly logged as having requested this certificate. \ -If you're running letsencrypt in manual mode on a machine that is not your server, \ +If you're running certbot in manual mode on a machine that is not your server, \ please ensure you're okay with that. Are you OK with your IP being logged? """ - # "cd /tmp/letsencrypt" makes sure user doesn't serve /root, + # "cd /tmp/certbot" makes sure user doesn't serve /root, # separate "public_html" ensures that cert.pem/key.pem are not # served and makes it more obvious that Python command will serve # anything recursively under the cwd @@ -80,7 +80,7 @@ s.serve_forever()" """ def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) self._root = (tempfile.mkdtemp() if self.conf("test-mode") - else "/tmp/letsencrypt") + else "/tmp/certbot") self._httpd = None @classmethod diff --git a/letsencrypt/plugins/manual_test.py b/certbot/plugins/manual_test.py similarity index 78% rename from letsencrypt/plugins/manual_test.py rename to certbot/plugins/manual_test.py index e749eb1f9..af1dc9909 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/certbot/plugins/manual_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.manual.""" +"""Tests for certbot.plugins.manual.""" import signal import unittest @@ -7,21 +7,21 @@ import mock from acme import challenges from acme import jose -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.manual.Authenticator.""" + """Tests for certbot.plugins.manual.Authenticator.""" def setUp(self): - from letsencrypt.plugins.manual import Authenticator + from certbot.plugins.manual import Authenticator self.config = mock.MagicMock( http01_port=8080, manual_test_mode=False, manual_public_ip_logging_ok=False, noninteractive_mode=True) @@ -48,8 +48,8 @@ class AuthenticatorTest(unittest.TestCase): def test_perform_empty(self): self.assertEqual([], self.auth.perform([])) - @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - @mock.patch("letsencrypt.plugins.manual.sys.stdout") + @mock.patch("certbot.plugins.manual.zope.component.getUtility") + @mock.patch("certbot.plugins.manual.sys.stdout") @mock.patch("acme.challenges.HTTP01Response.simple_verify") @mock.patch("__builtin__.raw_input") def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): @@ -66,12 +66,12 @@ class AuthenticatorTest(unittest.TestCase): self.assertTrue(self.achalls[0].chall.encode("token") in message) mock_verify.return_value = False - with mock.patch("letsencrypt.plugins.manual.logger") as mock_logger: + with mock.patch("certbot.plugins.manual.logger") as mock_logger: self.auth.perform(self.achalls) mock_logger.warning.assert_called_once_with(mock.ANY) - @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - @mock.patch("letsencrypt.plugins.manual.Authenticator._notify_and_wait") + @mock.patch("certbot.plugins.manual.zope.component.getUtility") + @mock.patch("certbot.plugins.manual.Authenticator._notify_and_wait") def test_disagree_with_ip_logging(self, mock_notify, mock_interaction): mock_interaction().yesno.return_value = False mock_notify.side_effect = errors.Error("Exception not raised, \ @@ -79,14 +79,14 @@ class AuthenticatorTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) - @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) + @mock.patch("certbot.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_command_oserror(self, mock_popen): mock_popen.side_effect = OSError self.assertEqual([False], self.auth_test_mode.perform(self.achalls)) - @mock.patch("letsencrypt.plugins.manual.socket.socket") - @mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True) - @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) + @mock.patch("certbot.plugins.manual.socket.socket") + @mock.patch("certbot.plugins.manual.time.sleep", autospec=True) + @mock.patch("certbot.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_command_run_failure( self, mock_popen, unused_mock_sleep, unused_mock_socket): mock_popen.poll.return_value = 10 @@ -100,7 +100,7 @@ class AuthenticatorTest(unittest.TestCase): httpd.poll.return_value = 0 self.auth_test_mode.cleanup(self.achalls) - @mock.patch("letsencrypt.plugins.manual.os.killpg", autospec=True) + @mock.patch("certbot.plugins.manual.os.killpg", autospec=True) def test_cleanup_test_mode_kills_still_running(self, mock_killpg): # pylint: disable=protected-access self.auth_test_mode._httpd = httpd = mock.Mock(pid=1234) diff --git a/letsencrypt/plugins/null.py b/certbot/plugins/null.py similarity index 94% rename from letsencrypt/plugins/null.py rename to certbot/plugins/null.py index 2c643d495..995b96274 100644 --- a/letsencrypt/plugins/null.py +++ b/certbot/plugins/null.py @@ -4,8 +4,8 @@ import logging import zope.component import zope.interface -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import interfaces +from certbot.plugins import common logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/null_test.py b/certbot/plugins/null_test.py similarity index 77% rename from letsencrypt/plugins/null_test.py rename to certbot/plugins/null_test.py index 008bb0381..305954a2f 100644 --- a/letsencrypt/plugins/null_test.py +++ b/certbot/plugins/null_test.py @@ -1,14 +1,14 @@ -"""Tests for letsencrypt.plugins.null.""" +"""Tests for certbot.plugins.null.""" import unittest import mock class InstallerTest(unittest.TestCase): - """Tests for letsencrypt.plugins.null.Installer.""" + """Tests for certbot.plugins.null.Installer.""" def setUp(self): - from letsencrypt.plugins.null import Installer + from certbot.plugins.null import Installer self.installer = Installer(config=mock.MagicMock(), name="null") def test_it(self): diff --git a/letsencrypt/plugins/selection.py b/certbot/plugins/selection.py similarity index 95% rename from letsencrypt/plugins/selection.py rename to certbot/plugins/selection.py index 20f6ac512..ac509d779 100644 --- a/letsencrypt/plugins/selection.py +++ b/certbot/plugins/selection.py @@ -6,10 +6,10 @@ import logging import zope.component -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.display import util as display_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) z_util = zope.component.getUtility @@ -33,7 +33,7 @@ def pick_installer(config, default, plugins, def pick_authenticator( config, default, plugins, question="How would you " - "like to authenticate with the Let's Encrypt CA?"): + "like to authenticate with the ACME CA?"): """Pick authentication plugin.""" return pick_plugin( config, default, plugins, question, (interfaces.IAuthenticator,)) @@ -42,9 +42,9 @@ def pick_authenticator( def pick_plugin(config, default, plugins, question, ifaces): """Pick plugin. - :param letsencrypt.interfaces.IConfig: Configuration + :param certbot.interfaces.IConfig: Configuration :param str default: Plugin name supplied by user or ``None``. - :param letsencrypt.plugins.disco.PluginsRegistry plugins: + :param certbot.plugins.disco.PluginsRegistry plugins: All plugins registered as entry points. :param str question: Question to be presented to the user in case multiple candidates are found. @@ -158,7 +158,7 @@ def choose_configurator_plugins(config, plugins, verb): # Which plugins do we need? if verb == "run": need_inst = need_auth = True - from letsencrypt.cli import cli_command + from certbot.cli import cli_command if req_auth in noninstaller_plugins and not req_inst: msg = ('With the {0} plugin, you probably want to use the "certonly" command, eg:{1}' '{1} {2} certonly --{0}{1}{1}' @@ -263,7 +263,7 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): if os.path.exists("/etc/debian_version"): # Debian... installers are at least possible msg = ('No installers seem to be present and working on your system; ' - 'fix that or try running letsencrypt with the "certonly" command') + 'fix that or try running certbot with the "certonly" command') else: # XXX update this logic as we make progress on #788 and nginx support msg = ('No installers are available on your OS yet; try running ' diff --git a/letsencrypt/plugins/selection_test.py b/certbot/plugins/selection_test.py similarity index 80% rename from letsencrypt/plugins/selection_test.py rename to certbot/plugins/selection_test.py index 0beaab076..001ca5cff 100644 --- a/letsencrypt/plugins/selection_test.py +++ b/certbot/plugins/selection_test.py @@ -5,40 +5,40 @@ import unittest import mock import zope.component -from letsencrypt.display import util as display_util -from letsencrypt import interfaces +from certbot.display import util as display_util +from certbot import interfaces class ConveniencePickPluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.pick_*.""" + """Tests for certbot.plugins.selection.pick_*.""" def _test(self, fun, ifaces): config = mock.Mock() default = mock.Mock() plugins = mock.Mock() - with mock.patch("letsencrypt.plugins.selection.pick_plugin") as mock_p: + with mock.patch("certbot.plugins.selection.pick_plugin") as mock_p: mock_p.return_value = "foo" self.assertEqual("foo", fun(config, default, plugins, "Question?")) mock_p.assert_called_once_with( config, default, plugins, "Question?", ifaces) def test_authenticator(self): - from letsencrypt.plugins.selection import pick_authenticator + from certbot.plugins.selection import pick_authenticator self._test(pick_authenticator, (interfaces.IAuthenticator,)) def test_installer(self): - from letsencrypt.plugins.selection import pick_installer + from certbot.plugins.selection import pick_installer self._test(pick_installer, (interfaces.IInstaller,)) def test_configurator(self): - from letsencrypt.plugins.selection import pick_configurator + from certbot.plugins.selection import pick_configurator self._test(pick_configurator, (interfaces.IAuthenticator, interfaces.IInstaller)) class PickPluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.pick_plugin.""" + """Tests for certbot.plugins.selection.pick_plugin.""" def setUp(self): self.config = mock.Mock(noninteractive_mode=False) @@ -48,7 +48,7 @@ class PickPluginTest(unittest.TestCase): self.ifaces = [] def _call(self): - from letsencrypt.plugins.selection import pick_plugin + from certbot.plugins.selection import pick_plugin return pick_plugin(self.config, self.default, self.reg, self.question, self.ifaces) @@ -89,7 +89,7 @@ class PickPluginTest(unittest.TestCase): "bar": plugin_ep, "baz": plugin_ep, } - with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + with mock.patch("certbot.plugins.selection.choose_plugin") as mock_choose: mock_choose.return_value = plugin_ep self.assertEqual("foo", self._call()) mock_choose.assert_called_once_with( @@ -101,13 +101,13 @@ class PickPluginTest(unittest.TestCase): "baz": None, } - with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + with mock.patch("certbot.plugins.selection.choose_plugin") as mock_choose: mock_choose.return_value = None self.assertTrue(self._call() is None) class ChoosePluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.choose_plugin.""" + """Tests for certbot.plugins.selection.choose_plugin.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -122,17 +122,17 @@ class ChoosePluginTest(unittest.TestCase): ] def _call(self): - from letsencrypt.plugins.selection import choose_plugin + from certbot.plugins.selection import choose_plugin return choose_plugin(self.plugins, "Question?") - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_selection(self, mock_util): mock_util().menu.side_effect = [(display_util.OK, 0), (display_util.OK, 1)] self.assertEqual(self.mock_stand, self._call()) self.assertEqual(mock_util().notification.call_count, 1) - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_more_info(self, mock_util): mock_util().menu.side_effect = [ (display_util.HELP, 0), @@ -143,7 +143,7 @@ class ChoosePluginTest(unittest.TestCase): self.assertEqual(self.mock_stand, self._call()) self.assertEqual(mock_util().notification.call_count, 2) - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_no_choice(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 0) self.assertTrue(self._call() is None) diff --git a/letsencrypt/plugins/standalone.py b/certbot/plugins/standalone.py similarity index 97% rename from letsencrypt/plugins/standalone.py rename to certbot/plugins/standalone.py index acc253bca..a3bb1d8f0 100644 --- a/letsencrypt/plugins/standalone.py +++ b/certbot/plugins/standalone.py @@ -12,11 +12,11 @@ import zope.interface from acme import challenges from acme import standalone as acme_standalone -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.plugins import common -from letsencrypt.plugins import util +from certbot.plugins import common +from certbot.plugins import util logger = logging.getLogger(__name__) @@ -91,7 +91,7 @@ class ServerManager(object): *instance.server.socket.getsockname()[:2]) instance.server.shutdown() # Not calling server_close causes problems when renewing multiple - # certs with `letsencrypt renew` using TLSSNI01 and PyOpenSSL 0.13 + # certs with `certbot renew` using TLSSNI01 and PyOpenSSL 0.13 instance.server.server_close() instance.thread.join() del self._instances[port] diff --git a/letsencrypt/plugins/standalone_test.py b/certbot/plugins/standalone_test.py similarity index 91% rename from letsencrypt/plugins/standalone_test.py rename to certbot/plugins/standalone_test.py index 80f9c8a74..9f5b14591 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/certbot/plugins/standalone_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.standalone.""" +"""Tests for certbot.plugins.standalone.""" import argparse import socket import unittest @@ -10,19 +10,19 @@ from acme import challenges from acme import jose from acme import standalone as acme_standalone -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import achallenges +from certbot import errors +from certbot import interfaces -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util class ServerManagerTest(unittest.TestCase): - """Tests for letsencrypt.plugins.standalone.ServerManager.""" + """Tests for certbot.plugins.standalone.ServerManager.""" def setUp(self): - from letsencrypt.plugins.standalone import ServerManager + from certbot.plugins.standalone import ServerManager self.certs = {} self.http_01_resources = {} self.mgr = ServerManager(self.certs, self.http_01_resources) @@ -68,7 +68,7 @@ class SupportedChallengesValidatorTest(unittest.TestCase): """Tests for plugins.standalone.supported_challenges_validator.""" def _call(self, data): - from letsencrypt.plugins.standalone import ( + from certbot.plugins.standalone import ( supported_challenges_validator) return supported_challenges_validator(data) @@ -87,10 +87,10 @@ class SupportedChallengesValidatorTest(unittest.TestCase): class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.standalone.Authenticator.""" + """Tests for certbot.plugins.standalone.Authenticator.""" def setUp(self): - from letsencrypt.plugins.standalone import Authenticator + from certbot.plugins.standalone import Authenticator self.config = mock.MagicMock( tls_sni_01_port=1234, http01_port=4321, standalone_supported_challenges="tls-sni-01,http-01") @@ -117,7 +117,7 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(self.auth.get_chall_pref(domain=None), [challenges.TLSSNI01]) - @mock.patch("letsencrypt.plugins.standalone.util") + @mock.patch("certbot.plugins.standalone.util") def test_perform_already_listening(self, mock_util): for chall, port in ((challenges.TLSSNI01.typ, 1234), (challenges.HTTP01.typ, 4321)): @@ -128,14 +128,14 @@ class AuthenticatorTest(unittest.TestCase): mock_util.already_listening.assert_called_once_with(port, False) mock_util.already_listening.reset_mock() - @mock.patch("letsencrypt.plugins.standalone.zope.component.getUtility") + @mock.patch("certbot.plugins.standalone.zope.component.getUtility") def test_perform(self, unused_mock_get_utility): achalls = [1, 2, 3] self.auth.perform2 = mock.Mock(return_value=mock.sentinel.responses) self.assertEqual(mock.sentinel.responses, self.auth.perform(achalls)) self.auth.perform2.assert_called_once_with(achalls) - @mock.patch("letsencrypt.plugins.standalone.zope.component.getUtility") + @mock.patch("certbot.plugins.standalone.zope.component.getUtility") def _test_perform_bind_errors(self, errno, achalls, mock_get_utility): def _perform2(unused_achalls): raise errors.StandaloneBindError(mock.Mock(errno=errno), 1234) diff --git a/letsencrypt/plugins/util.py b/certbot/plugins/util.py similarity index 98% rename from letsencrypt/plugins/util.py rename to certbot/plugins/util.py index 3382b73dd..5fc98dff6 100644 --- a/letsencrypt/plugins/util.py +++ b/certbot/plugins/util.py @@ -5,7 +5,7 @@ import socket import psutil import zope.component -from letsencrypt import interfaces +from certbot import interfaces logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/util_test.py b/certbot/plugins/util_test.py similarity index 81% rename from letsencrypt/plugins/util_test.py rename to certbot/plugins/util_test.py index 1591976b0..9bc8793c7 100644 --- a/letsencrypt/plugins/util_test.py +++ b/certbot/plugins/util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.util.""" +"""Tests for certbot.plugins.util.""" import unittest import mock @@ -6,14 +6,14 @@ import psutil class AlreadyListeningTest(unittest.TestCase): - """Tests for letsencrypt.plugins.already_listening.""" + """Tests for certbot.plugins.already_listening.""" def _call(self, *args, **kwargs): - from letsencrypt.plugins.util import already_listening + from certbot.plugins.util import already_listening return already_listening(*args, **kwargs) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_race_condition(self, mock_get_utility, mock_process, mock_net): # This tests a race condition, or permission problem, or OS # incompatibility in which, for some reason, no process name can be @@ -36,9 +36,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.generic_notification.call_count, 0) mock_process.assert_called_once_with(4416) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_not_listening(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -54,9 +54,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.generic_notification.call_count, 0) self.assertEqual(mock_process.call_count, 0) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_listening_ipv4(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -75,9 +75,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.call_count, 1) mock_process.assert_called_once_with(4416) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_listening_ipv6(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -98,7 +98,7 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.call_count, 1) mock_process.assert_called_once_with(4420) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.net_connections") def test_access_denied_exception(self, mock_net): mock_net.side_effect = psutil.AccessDenied("") self.assertFalse(self._call(12345)) diff --git a/certbot/plugins/webroot.py b/certbot/plugins/webroot.py new file mode 100644 index 000000000..fbe703f40 --- /dev/null +++ b/certbot/plugins/webroot.py @@ -0,0 +1,295 @@ +"""Webroot plugin.""" +import argparse +import collections +import errno +import json +import logging +import os + +import six +import zope.component +import zope.interface + +from acme import challenges + +from certbot import cli +from certbot import errors +from certbot import interfaces +from certbot.display import util as display_util +from certbot.plugins import common + + +logger = logging.getLogger(__name__) + + +@zope.interface.implementer(interfaces.IAuthenticator) +@zope.interface.provider(interfaces.IPluginFactory) +class Authenticator(common.Plugin): + """Webroot Authenticator.""" + + description = "Place files in webroot directory" + + MORE_INFO = """\ +Authenticator plugin that performs http-01 challenge by saving +necessary validation resources to appropriate paths on the file +system. It expects that there is some other HTTP server configured +to serve all files under specified web root ({0}).""" + + def more_info(self): # pylint: disable=missing-docstring,no-self-use + return self.MORE_INFO.format(self.conf("path")) + + @classmethod + def add_parser_arguments(cls, add): + add("path", "-w", default=[], action=_WebrootPathAction, + help="public_html / webroot path. This can be specified multiple " + "times to handle different domains; each domain will have " + "the webroot path that preceded it. For instance: `-w " + "/var/www/example -d example.com -d www.example.com -w " + "/var/www/thing -d thing.net -d m.thing.net`") + add("map", default={}, action=_WebrootMapAction, + help="JSON dictionary mapping domains to webroot paths; this " + "implies -d for each entry. You may need to escape this from " + "your shell. E.g.: --webroot-map " + '\'{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}\' ' + "This option is merged with, but takes precedence over, -w / " + "-d entries. At present, if you put webroot-map in a config " + "file, it needs to be on a single line, like: webroot-map = " + '{"example.com":"/var/www"}.') + + def get_chall_pref(self, domain): # pragma: no cover + # pylint: disable=missing-docstring,no-self-use,unused-argument + return [challenges.HTTP01] + + def __init__(self, *args, **kwargs): + super(Authenticator, self).__init__(*args, **kwargs) + self.full_roots = {} + self.performed = collections.defaultdict(set) + + def prepare(self): # pylint: disable=missing-docstring + pass + + def perform(self, achalls): # pylint: disable=missing-docstring + self._set_webroots(achalls) + + self._create_challenge_dirs() + + return [self._perform_single(achall) for achall in achalls] + + def _set_webroots(self, achalls): + if self.conf("path"): + webroot_path = self.conf("path")[-1] + logger.info("Using the webroot path %s for all unmatched domains.", + webroot_path) + for achall in achalls: + self.conf("map").setdefault(achall.domain, webroot_path) + else: + known_webroots = list(set(six.itervalues(self.conf("map")))) + for achall in achalls: + if achall.domain not in self.conf("map"): + new_webroot = self._prompt_for_webroot(achall.domain, + known_webroots) + # Put the most recently input + # webroot first for easy selection + try: + known_webroots.remove(new_webroot) + except ValueError: + pass + known_webroots.insert(0, new_webroot) + self.conf("map")[achall.domain] = new_webroot + + def _prompt_for_webroot(self, domain, known_webroots): + webroot = None + + while webroot is None: + webroot = self._prompt_with_webroot_list(domain, known_webroots) + + if webroot is None: + webroot = self._prompt_for_new_webroot(domain) + + return webroot + + def _prompt_with_webroot_list(self, domain, known_webroots): + display = zope.component.getUtility(interfaces.IDisplay) + + while True: + code, index = display.menu( + "Select the webroot for {0}:".format(domain), + ["Enter a new webroot"] + known_webroots, + help_label="Help", cli_flag="--" + self.option_name("path")) + if code == display_util.CANCEL: + raise errors.PluginError( + "Every requested domain must have a " + "webroot when using the webroot plugin.") + elif code == display_util.HELP: + display.notification( + "To use the webroot plugin, you need to have an " + "HTTP server running on this system serving files " + "for the requested domain. Additionally, this " + "server should be serving all files contained in a " + "public_html or webroot directory. The webroot " + "plugin works by temporarily saving necessary " + "resources in the HTTP server's webroot directory " + "to pass domain validation challenges.") + else: # code == display_util.OK + return None if index == 0 else known_webroots[index - 1] + + def _prompt_for_new_webroot(self, domain): + display = zope.component.getUtility(interfaces.IDisplay) + + while True: + code, webroot = display.directory_select( + "Input the webroot for {0}:".format(domain)) + if code == display_util.HELP: + # Help can currently only be selected + # when using the ncurses interface + display.notification(display_util.DSELECT_HELP) + elif code == display_util.CANCEL: + return None + else: # code == display_util.OK + try: + return _validate_webroot(webroot) + except errors.PluginError as error: + display.notification(str(error), pause=False) + + def _create_challenge_dirs(self): + path_map = self.conf("map") + if not path_map: + raise errors.PluginError( + "Missing parts of webroot configuration; please set either " + "--webroot-path and --domains, or --webroot-map. Run with " + " --help webroot for examples.") + for name, path in path_map.items(): + self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) + + logger.debug("Creating root challenges validation dir at %s", + self.full_roots[name]) + + # Change the permissions to be writable (GH #1389) + # Umask is used instead of chmod to ensure the client can also + # run as non-root (GH #1795) + old_umask = os.umask(0o022) + + try: + # This is coupled with the "umask" call above because + # os.makedirs's "mode" parameter may not always work: + # https://stackoverflow.com/questions/5231901/permission-problems-when-creating-a-dir-with-os-makedirs-python + os.makedirs(self.full_roots[name], 0o0755) + + # Set owner as parent directory if possible + try: + stat_path = os.stat(path) + os.chown(self.full_roots[name], stat_path.st_uid, + stat_path.st_gid) + except OSError as exception: + if exception.errno == errno.EACCES: + logger.debug("Insufficient permissions to change owner and uid - ignoring") + else: + raise errors.PluginError( + "Couldn't create root for {0} http-01 " + "challenge responses: {1}", name, exception) + + except OSError as exception: + if exception.errno != errno.EEXIST: + raise errors.PluginError( + "Couldn't create root for {0} http-01 " + "challenge responses: {1}", name, exception) + finally: + os.umask(old_umask) + + def _get_validation_path(self, root_path, achall): + return os.path.join(root_path, achall.chall.encode("token")) + + def _perform_single(self, achall): + response, validation = achall.response_and_validation() + + root_path = self.full_roots[achall.domain] + validation_path = self._get_validation_path(root_path, achall) + logger.debug("Attempting to save validation to %s", validation_path) + + # Change permissions to be world-readable, owner-writable (GH #1795) + old_umask = os.umask(0o022) + + try: + with open(validation_path, "w") as validation_file: + validation_file.write(validation.encode()) + finally: + os.umask(old_umask) + + self.performed[root_path].add(achall) + + return response + + def cleanup(self, achalls): # pylint: disable=missing-docstring + for achall in achalls: + root_path = self.full_roots.get(achall.domain, None) + if root_path is not None: + validation_path = self._get_validation_path(root_path, achall) + logger.debug("Removing %s", validation_path) + os.remove(validation_path) + self.performed[root_path].remove(achall) + + for root_path, achalls in six.iteritems(self.performed): + if not achalls: + try: + os.rmdir(root_path) + logger.debug("All challenges cleaned up, removing %s", + root_path) + except OSError as exc: + if exc.errno == errno.ENOTEMPTY: + logger.debug("Challenges cleaned up but %s not empty", + root_path) + elif exc.errno == errno.EACCES: + logger.debug("Challenges cleaned up but no permissions for %s", + root_path) + else: + raise + + +class _WebrootMapAction(argparse.Action): + """Action class for parsing webroot_map.""" + + def __call__(self, parser, namespace, webroot_map, option_string=None): + for domains, webroot_path in six.iteritems(json.loads(webroot_map)): + webroot_path = _validate_webroot(webroot_path) + namespace.webroot_map.update( + (d, webroot_path) for d in cli.add_domains(namespace, domains)) + + +class _WebrootPathAction(argparse.Action): + """Action class for parsing webroot_path.""" + + def __init__(self, *args, **kwargs): + super(_WebrootPathAction, self).__init__(*args, **kwargs) + self._domain_before_webroot = False + + def __call__(self, parser, namespace, webroot_path, option_string=None): + if self._domain_before_webroot: + raise errors.PluginError( + "If you specify multiple webroot paths, " + "one of them must precede all domain flags") + + if namespace.webroot_path: + # Apply previous webroot to all matched + # domains before setting the new webroot path + prev_webroot = namespace.webroot_path[-1] + for domain in namespace.domains: + namespace.webroot_map.setdefault(domain, prev_webroot) + elif namespace.domains: + self._domain_before_webroot = True + + namespace.webroot_path.append(_validate_webroot(webroot_path)) + + +def _validate_webroot(webroot_path): + """Validates and returns the absolute path of webroot_path. + + :param str webroot_path: path to the webroot directory + + :returns: absolute path of webroot_path + :rtype: str + + """ + if not os.path.isdir(webroot_path): + raise errors.PluginError(webroot_path + " does not exist or is not a directory") + + return os.path.abspath(webroot_path) diff --git a/certbot/plugins/webroot_test.py b/certbot/plugins/webroot_test.py new file mode 100644 index 000000000..3f429ec34 --- /dev/null +++ b/certbot/plugins/webroot_test.py @@ -0,0 +1,276 @@ +"""Tests for certbot.plugins.webroot.""" + +from __future__ import print_function + +import argparse +import errno +import os +import shutil +import stat +import tempfile +import unittest + +import mock +import six + +from acme import challenges +from acme import jose + +from certbot import achallenges +from certbot import errors +from certbot.display import util as display_util + +from certbot.tests import acme_util +from certbot.tests import test_util + + +KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) + + +class AuthenticatorTest(unittest.TestCase): + """Tests for certbot.plugins.webroot.Authenticator.""" + + achall = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) + + def setUp(self): + from certbot.plugins.webroot import Authenticator + self.path = tempfile.mkdtemp() + self.root_challenge_path = os.path.join( + self.path, ".well-known", "acme-challenge") + self.validation_path = os.path.join( + self.root_challenge_path, + "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") + self.config = mock.MagicMock(webroot_path=self.path, + webroot_map={"thing.com": self.path}) + self.auth = Authenticator(self.config, "webroot") + + def tearDown(self): + shutil.rmtree(self.path) + + def test_more_info(self): + more_info = self.auth.more_info() + self.assertTrue(isinstance(more_info, str)) + self.assertTrue(self.path in more_info) + + def test_add_parser_arguments(self): + add = mock.MagicMock() + self.auth.add_parser_arguments(add) + self.assertEqual(2, add.call_count) + + def test_prepare(self): + self.auth.prepare() # shouldn't raise any exceptions + + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") + def test_webroot_from_list(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {"otherthing.com": self.path} + mock_display = mock_get_utility() + mock_display.menu.return_value = (display_util.OK, 1,) + + self.auth.perform([self.achall]) + self.assertTrue(mock_display.menu.called) + for call in mock_display.menu.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + self.assertTrue(all( + webroot in call[0][1] + for webroot in six.itervalues(self.config.webroot_map))) + self.assertEqual(self.config.webroot_map[self.achall.domain], + self.path) + + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") + def test_webroot_from_list_help_and_cancel(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {"otherthing.com": self.path} + + mock_display = mock_get_utility() + mock_display.menu.side_effect = ((display_util.HELP, -1), + (display_util.CANCEL, -1),) + self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) + self.assertTrue(mock_display.notification.called) + self.assertTrue(mock_display.menu.called) + for call in mock_display.menu.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + self.assertTrue(all( + webroot in call[0][1] + for webroot in six.itervalues(self.config.webroot_map))) + + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") + def test_new_webroot(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {} + + imaginary_dir = os.path.join(os.sep, "imaginary", "dir") + + mock_display = mock_get_utility() + mock_display.menu.return_value = (display_util.OK, 0,) + mock_display.directory_select.side_effect = ( + (display_util.HELP, -1,), (display_util.CANCEL, -1,), + (display_util.OK, imaginary_dir,), (display_util.OK, self.path,),) + self.auth.perform([self.achall]) + + self.assertTrue(mock_display.notification.called) + for call in mock_display.notification.call_args_list: + self.assertTrue(imaginary_dir in call[0][0] or + display_util.DSELECT_HELP == call[0][0]) + + self.assertTrue(mock_display.directory_select.called) + for call in mock_display.directory_select.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + + def test_perform_missing_root(self): + self.config.webroot_path = None + self.config.webroot_map = {} + self.assertRaises(errors.PluginError, self.auth.perform, []) + + def test_perform_reraises_other_errors(self): + self.auth.full_path = os.path.join(self.path, "null") + permission_canary = os.path.join(self.path, "rnd") + with open(permission_canary, "w") as f: + f.write("thingimy") + os.chmod(self.path, 0o000) + try: + open(permission_canary, "r") + print("Warning, running tests as root skips permissions tests...") + except IOError: + # ok, permissions work, test away... + self.assertRaises(errors.PluginError, self.auth.perform, []) + os.chmod(self.path, 0o700) + + @mock.patch("certbot.plugins.webroot.os.chown") + def test_failed_chown_eacces(self, mock_chown): + mock_chown.side_effect = OSError(errno.EACCES, "msg") + self.auth.perform([self.achall]) # exception caught and logged + + @mock.patch("certbot.plugins.webroot.os.chown") + def test_failed_chown_not_eacces(self, mock_chown): + mock_chown.side_effect = OSError() + self.assertRaises(errors.PluginError, self.auth.perform, []) + + def test_perform_permissions(self): + self.auth.prepare() + + # Remove exec bit from permission check, so that it + # matches the file + self.auth.perform([self.achall]) + path_permissions = stat.S_IMODE(os.stat(self.validation_path).st_mode) + self.assertEqual(path_permissions, 0o644) + + # Check permissions of the directories + + for dirpath, dirnames, _ in os.walk(self.path): + for directory in dirnames: + full_path = os.path.join(dirpath, directory) + dir_permissions = stat.S_IMODE(os.stat(full_path).st_mode) + self.assertEqual(dir_permissions, 0o755) + + parent_gid = os.stat(self.path).st_gid + parent_uid = os.stat(self.path).st_uid + + self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid) + self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid) + + def test_perform_cleanup(self): + self.auth.prepare() + responses = self.auth.perform([self.achall]) + self.assertEqual(1, len(responses)) + self.assertTrue(os.path.exists(self.validation_path)) + with open(self.validation_path) as validation_f: + validation = validation_f.read() + self.assertTrue( + challenges.KeyAuthorizationChallengeResponse( + key_authorization=validation).verify( + self.achall.chall, KEY.public_key())) + + self.auth.cleanup([self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertFalse(os.path.exists(self.root_challenge_path)) + + def test_cleanup_leftovers(self): + self.auth.prepare() + self.auth.perform([self.achall]) + + leftover_path = os.path.join(self.root_challenge_path, 'leftover') + os.mkdir(leftover_path) + + self.auth.cleanup([self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertTrue(os.path.exists(self.root_challenge_path)) + + os.rmdir(leftover_path) + + @mock.patch('os.rmdir') + def test_cleanup_permission_denied(self, mock_rmdir): + self.auth.prepare() + self.auth.perform([self.achall]) + + os_error = OSError() + os_error.errno = errno.EACCES + mock_rmdir.side_effect = os_error + + self.auth.cleanup([self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertTrue(os.path.exists(self.root_challenge_path)) + + @mock.patch('os.rmdir') + def test_cleanup_oserror(self, mock_rmdir): + self.auth.prepare() + self.auth.perform([self.achall]) + + os_error = OSError() + os_error.errno = errno.ENOENT + mock_rmdir.side_effect = os_error + + self.assertRaises(OSError, self.auth.cleanup, [self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertTrue(os.path.exists(self.root_challenge_path)) + + +class WebrootActionTest(unittest.TestCase): + """Tests for webroot argparse actions.""" + + achall = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) + + def setUp(self): + from certbot.plugins.webroot import Authenticator + self.path = tempfile.mkdtemp() + self.parser = argparse.ArgumentParser() + self.parser.add_argument("-d", "--domains", + action="append", default=[]) + Authenticator.inject_parser_options(self.parser, "webroot") + + def test_webroot_map_action(self): + args = self.parser.parse_args( + ["--webroot-map", '{{"thing.com":"{0}"}}'.format(self.path)]) + self.assertEqual(args.webroot_map["thing.com"], self.path) + + def test_domain_before_webroot(self): + args = self.parser.parse_args( + "-d {0} -w {1}".format(self.achall.domain, self.path).split()) + config = self._get_config_after_perform(args) + self.assertEqual(config.webroot_map[self.achall.domain], self.path) + + def test_domain_before_webroot_error(self): + self.assertRaises(errors.PluginError, self.parser.parse_args, + "-d foo -w bar -w baz".split()) + self.assertRaises(errors.PluginError, self.parser.parse_args, + "-d foo -w bar -d baz -w qux".split()) + + def test_multiwebroot(self): + args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( + self.path, self.achall.domain, tempfile.mkdtemp()).split()) + self.assertEqual(args.webroot_map[self.achall.domain], self.path) + config = self._get_config_after_perform(args) + self.assertEqual( + config.webroot_map[self.achall.domain], self.path) + + def _get_config_after_perform(self, config): + from certbot.plugins.webroot import Authenticator + auth = Authenticator(config, "webroot") + auth.perform([self.achall]) + return auth.config + + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/letsencrypt/renewal.py b/certbot/renewal.py similarity index 71% rename from letsencrypt/renewal.py rename to certbot/renewal.py index 27546bec9..180499387 100644 --- a/letsencrypt/renewal.py +++ b/certbot/renewal.py @@ -9,11 +9,19 @@ import traceback import six import zope.component -from letsencrypt import configuration -from letsencrypt import cli -from letsencrypt import errors -from letsencrypt import storage -from letsencrypt.plugins import disco as plugins_disco +import OpenSSL + +from certbot import configuration +from certbot import cli +from certbot import constants + +from certbot import crypto_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot import hooks +from certbot import storage +from certbot.plugins import disco as plugins_disco logger = logging.getLogger(__name__) @@ -72,14 +80,14 @@ def _reconstitute(config, full_path): _restore_plugin_configs(config, renewalparams) except (ValueError, errors.Error) as error: logger.warning( - "An error occured while parsing %s. The error was %s. " + "An error occurred while parsing %s. The error was %s. " "Skipping the file.", full_path, error.message) logger.debug("Traceback was:\n%s", traceback.format_exc()) return None try: - for d in renewal_candidate.names(): - cli.process_domain(config, d) + config.domains = [le_util.enforce_domain_sanity(d) + for d in renewal_candidate.names()] except errors.ConfigurationError as error: logger.warning("Renewal configuration file %s references a cert " "that contains an invalid domain name. The problem " @@ -96,16 +104,14 @@ def _restore_webroot_config(config, renewalparams): form. """ if "webroot_map" in renewalparams: - # if the user does anything that would create a new webroot map on the - # CLI, don't use the old one - if not (cli.set_by_cli("webroot_map") or cli.set_by_cli("webroot_path")): - setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) + if not cli.set_by_cli("webroot_map"): + config.namespace.webroot_map = renewalparams["webroot_map"] elif "webroot_path" in renewalparams: logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") wp = renewalparams["webroot_path"] if isinstance(wp, str): # prior to 0.1.0, webroot_path was a string wp = [wp] - setattr(config.namespace, "webroot_path", wp) + config.namespace.webroot_path = wp def _restore_plugin_configs(config, renewalparams): @@ -199,41 +205,97 @@ def should_renew(config, lineage): return False +def _avoid_invalidating_lineage(config, lineage, original_server): + "Do not renew a valid cert with one from a staging server!" + def _is_staging(srv): + return srv == constants.STAGING_URI or "staging" in srv + + # Some lineages may have begun with --staging, but then had production certs + # added to them + latest_cert = OpenSSL.crypto.load_certificate( + OpenSSL.crypto.FILETYPE_PEM, open(lineage.cert).read()) + # all our test certs are from happy hacker fake CA, though maybe one day + # we should test more methodically + now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() + + if _is_staging(config.server): + if not _is_staging(original_server) or now_valid: + if not config.break_my_certs: + names = ", ".join(lineage.names()) + raise errors.Error( + "You've asked to renew/replace a seemingly valid certificate with " + "a test certificate (domains: {0}). We will not do that " + "unless you use the --break-my-certs flag!".format(names)) + + +def renew_cert(config, domains, le_client, lineage): + "Renew a certificate lineage." + renewal_params = lineage.configuration["renewalparams"] + original_server = renewal_params.get("server", cli.flag_default("server")) + _avoid_invalidating_lineage(config, lineage, original_server) + new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) + if config.dry_run: + logger.info("Dry run: skipping updating lineage at %s", + os.path.dirname(lineage.cert)) + else: + prior_version = lineage.latest_common_version() + new_cert = OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped) + new_chain = crypto_util.dump_pyopenssl_chain(new_chain) + renewal_conf = configuration.RenewerConfiguration(config.namespace) + # TODO: Check return value of save_successor + lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf) + lineage.update_all_links_to(lineage.latest_common_version()) + + hooks.renew_hook(config, domains, lineage.live_dir) + + +def report(msgs, category): + "Format a results report for a category of renewal outcomes" + lines = ("%s (%s)" % (m, category) for m in msgs) + return " " + "\n ".join(lines) + def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): - def _status(msgs, category): - return " " + "\n ".join("%s (%s)" % (m, category) for m in msgs) + + out = [] + notify = out.append + if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates below have not been saved.)") - print() + notify("** DRY RUN: simulating 'certbot renew' close to cert expiry") + notify("** (The test certificates below have not been saved.)") + notify("") if renew_skipped: - print("The following certs are not due for renewal yet:") - print(_status(renew_skipped, "skipped")) + notify("The following certs are not due for renewal yet:") + notify(report(renew_skipped, "skipped")) if not renew_successes and not renew_failures: - print("No renewals were attempted.") + notify("No renewals were attempted.") elif renew_successes and not renew_failures: - print("Congratulations, all renewals succeeded. The following certs " - "have been renewed:") - print(_status(renew_successes, "success")) + notify("Congratulations, all renewals succeeded. The following certs " + "have been renewed:") + notify(report(renew_successes, "success")) elif renew_failures and not renew_successes: - print("All renewal attempts failed. The following certs could not be " - "renewed:") - print(_status(renew_failures, "failure")) + notify("All renewal attempts failed. The following certs could not be " + "renewed:") + notify(report(renew_failures, "failure")) elif renew_failures and renew_successes: - print("The following certs were successfully renewed:") - print(_status(renew_successes, "success")) - print("\nThe following certs could not be renewed:") - print(_status(renew_failures, "failure")) + notify("The following certs were successfully renewed:") + notify(report(renew_successes, "success")) + notify("\nThe following certs could not be renewed:") + notify(report(renew_failures, "failure")) if parse_failures: - print("\nAdditionally, the following renewal configuration files " - "were invalid: ") - print(_status(parse_failures, "parsefail")) + notify("\nAdditionally, the following renewal configuration files " + "were invalid: ") + notify(parse_failures, "parsefail") if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates above have not been saved.)") + notify("** DRY RUN: simulating 'certbot renew' close to cert expiry") + notify("** (The test certificates above have not been saved.)") + + if config.quiet and not (renew_failures or parse_failures): + return + print("\n".join(out)) def renew_all_lineages(config): @@ -253,7 +315,8 @@ def renew_all_lineages(config): renew_skipped = [] parse_failures = [] for renewal_file in renewal_conf_files(renewer_config): - print("Processing " + renewal_file) + disp = zope.component.getUtility(interfaces.IDisplay) + disp.notification("Processing " + renewal_file, pause=False) lineage_config = copy.deepcopy(config) # Note that this modifies config (to add back the configuration @@ -275,7 +338,7 @@ def renew_all_lineages(config): zope.component.provideUtility(lineage_config) if should_renew(lineage_config, renewal_candidate): plugins = plugins_disco.PluginsRegistry.find_all() - from letsencrypt import main + from certbot import main main.obtain_cert(lineage_config, plugins, renewal_candidate) renew_successes.append(renewal_candidate.fullchain) else: diff --git a/letsencrypt/reporter.py b/certbot/reporter.py similarity index 80% rename from letsencrypt/reporter.py rename to certbot/reporter.py index 147928e3c..d509cb0b8 100644 --- a/letsencrypt/reporter.py +++ b/certbot/reporter.py @@ -10,8 +10,8 @@ import textwrap from six.moves import queue # pylint: disable=import-error import zope.interface -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) @@ -35,8 +35,9 @@ class Reporter(object): _msg_type = collections.namedtuple('ReporterMsg', 'priority text on_crash') - def __init__(self): + def __init__(self, config): self.messages = queue.PriorityQueue() + self.config = config def add_message(self, msg, priority, on_crash=True): """Adds msg to the list of messages to be printed. @@ -76,9 +77,10 @@ class Reporter(object): if not self.messages.empty(): no_exception = sys.exc_info()[0] is None bold_on = sys.stdout.isatty() - if bold_on: - print(le_util.ANSI_SGR_BOLD) - print('IMPORTANT NOTES:') + if not self.config.quiet: + if bold_on: + print(le_util.ANSI_SGR_BOLD) + print('IMPORTANT NOTES:') first_wrapper = textwrap.TextWrapper( initial_indent=' - ', subsequent_indent=(' ' * 3)) next_wrapper = textwrap.TextWrapper( @@ -86,14 +88,20 @@ class Reporter(object): subsequent_indent=first_wrapper.subsequent_indent) while not self.messages.empty(): msg = self.messages.get() + if self.config.quiet: + # In --quiet mode, we only print high priority messages that + # are flagged for crash cases + if not (msg.priority == self.HIGH_PRIORITY and msg.on_crash): + continue if no_exception or msg.on_crash: if bold_on and msg.priority > self.HIGH_PRIORITY: - sys.stdout.write(le_util.ANSI_SGR_RESET) - bold_on = False + if not self.config.quiet: + sys.stdout.write(le_util.ANSI_SGR_RESET) + bold_on = False lines = msg.text.splitlines() print(first_wrapper.fill(lines[0])) if len(lines) > 1: print("\n".join( next_wrapper.fill(line) for line in lines[1:])) - if bold_on: + if bold_on and not self.config.quiet: sys.stdout.write(le_util.ANSI_SGR_RESET) diff --git a/letsencrypt/reverter.py b/certbot/reverter.py similarity index 87% rename from letsencrypt/reverter.py rename to certbot/reverter.py index ea54a91ee..fe6d9f24f 100644 --- a/letsencrypt/reverter.py +++ b/certbot/reverter.py @@ -1,18 +1,20 @@ """Reverter class saves configuration checkpoints and allows for recovery.""" import csv +import glob import logging import os import shutil import time +import traceback import zope.component -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import errors +from certbot import interfaces +from certbot import le_util -from letsencrypt.display import util as display_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -24,7 +26,7 @@ class Reverter(object): .. note:: Consider moving everything over to CSV format. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ def __init__(self, config): @@ -78,7 +80,7 @@ class Reverter(object): if not backups: logger.warning( - "Let's Encrypt hasn't modified your configuration, so rollback " + "Certbot hasn't modified your configuration, so rollback " "isn't available.") elif len(backups) < rollback: logger.warning("Unable to rollback %d checkpoints, only %d exist", @@ -98,7 +100,7 @@ class Reverter(object): """Displays all saved checkpoints. All checkpoints are printed by - :meth:`letsencrypt.interfaces.IDisplay.notification`. + :meth:`certbot.interfaces.IDisplay.notification`. .. todo:: Decide on a policy for error handling, OSError IOError... @@ -110,8 +112,7 @@ class Reverter(object): if num: backups = backups[:num] if not backups: - logger.info("The Let's Encrypt client has not saved any backups " - "of your configuration") + logger.info("Certbot has not saved backups of your configuration") return # Make sure there isn't anything unexpected in the backup folder @@ -289,7 +290,7 @@ class Reverter(object): :param set save_files: Set of files about to be saved. - :raises letsencrypt.errors.ReverterError: + :raises certbot.errors.ReverterError: when save is attempting to overwrite a temporary file. """ @@ -315,7 +316,7 @@ class Reverter(object): "file - %s" % filename) def register_file_creation(self, temporary, *files): - r"""Register the creation of all files during letsencrypt execution. + r"""Register the creation of all files during certbot execution. Call this method before writing to the file to make sure that the file will be cleaned up if the program exits unexpectedly. @@ -325,7 +326,7 @@ class Reverter(object): a temp or permanent save. :param \*files: file paths (str) to be registered - :raises letsencrypt.errors.ReverterError: If + :raises certbot.errors.ReverterError: If call does not contain necessary parameters or if the file creation is unable to be registered. @@ -333,16 +334,14 @@ class Reverter(object): # Make sure some files are provided... as this is an error # Made this mistake in my initial implementation of apache.dvsni.py if not files: - raise errors.ReverterError( - "Forgot to provide files to registration call") + raise errors.ReverterError("Forgot to provide files to registration call") cp_dir = self._get_cp_dir(temporary) # Append all new files (that aren't already registered) new_fd = None try: - new_fd, ex_files = self._read_and_append( - os.path.join(cp_dir, "NEW_FILES")) + new_fd, ex_files = self._read_and_append(os.path.join(cp_dir, "NEW_FILES")) for path in files: if path not in ex_files: @@ -439,7 +438,7 @@ class Reverter(object): :returns: Success :rtype: bool - :raises letsencrypt.errors.ReverterError: If + :raises certbot.errors.ReverterError: If all files within file_list cannot be removed """ @@ -477,52 +476,73 @@ class Reverter(object): :param str title: Title describing checkpoint - :raises letsencrypt.errors.ReverterError: when the + :raises certbot.errors.ReverterError: when the checkpoint is not able to be finalized. """ - # Adds title to self.config.in_progress_dir CHANGES_SINCE - # Move self.config.in_progress_dir to Backups directory and - # rename the directory as a timestamp # Check to make sure an "in progress" directory exists if not os.path.isdir(self.config.in_progress_dir): return - changes_since_path = os.path.join( - self.config.in_progress_dir, "CHANGES_SINCE") + changes_since_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE") + changes_since_tmp_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE.tmp") - changes_since_tmp_path = os.path.join( - self.config.in_progress_dir, "CHANGES_SINCE.tmp") + if not os.path.exists(changes_since_path): + logger.info("Rollback checkpoint is empty (no changes made?)") + with open(self.config.changes_since_path) as f: + f.write("No changes\n") + # Add title to self.config.in_progress_dir CHANGES_SINCE try: with open(changes_since_tmp_path, "w") as changes_tmp: changes_tmp.write("-- %s --\n" % title) with open(changes_since_path, "r") as changes_orig: changes_tmp.write(changes_orig.read()) + # Move self.config.in_progress_dir to Backups directory shutil.move(changes_since_tmp_path, changes_since_path) except (IOError, OSError): logger.error("Unable to finalize checkpoint - adding title") + logger.debug("Exception was:\n%s", traceback.format_exc()) raise errors.ReverterError("Unable to add title") + # rename the directory as a timestamp self._timestamp_progress_dir() + def _checkpoint_timestamp(self): + "Determine the timestamp of the checkpoint, enforcing monotonicity." + timestamp = str(time.time()) + others = glob.glob(os.path.join(self.config.backup_dir, "[0-9]*")) + others = [os.path.basename(d) for d in others] + others.append(timestamp) + others.sort() + if others[-1] != timestamp: + timetravel = str(float(others[-1]) + 1) + logger.warn("Current timestamp %s does not correspond to newest reverter " + "checkpoint; your clock probably jumped. Time travelling to %s", + timestamp, timetravel) + timestamp = timetravel + elif len(others) > 1 and others[-2] == timestamp: + # It is possible if the checkpoints are made extremely quickly + # that will result in a name collision. + logger.debug("Race condition with timestamp %s, incrementing by 0.01", timestamp) + timetravel = str(float(others[-1]) + 0.01) + timestamp = timetravel + return timestamp + def _timestamp_progress_dir(self): """Timestamp the checkpoint.""" # It is possible save checkpoints faster than 1 per second resulting in # collisions in the naming convention. - cur_time = time.time() - for _ in xrange(10): - final_dir = os.path.join(self.config.backup_dir, str(cur_time)) + for _ in xrange(2): + timestamp = self._checkpoint_timestamp() + final_dir = os.path.join(self.config.backup_dir, timestamp) try: os.rename(self.config.in_progress_dir, final_dir) return except OSError: - # It is possible if the checkpoints are made extremely quickly - # that will result in a name collision. - # If so, increment and try again - cur_time += .01 + logger.warning("Extreme, unexpected race condition, retrying (%s)", timestamp) # After 10 attempts... something is probably wrong here... logger.error( diff --git a/letsencrypt/storage.py b/certbot/storage.py similarity index 96% rename from letsencrypt/storage.py rename to certbot/storage.py index 59daa1a0d..4ef614a8e 100644 --- a/letsencrypt/storage.py +++ b/certbot/storage.py @@ -8,11 +8,11 @@ import configobj import parsedatetime import pytz -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import le_util +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import error_handler +from certbot import le_util logger = logging.getLogger(__name__) @@ -50,10 +50,11 @@ def add_time_interval(base_time, interval, textparser=parsedatetime.Calendar()): return textparser.parseDT(interval, base_time, tzinfo=tzinfo)[0] -def write_renewal_config(filename, target, relevant_data): +def write_renewal_config(o_filename, n_filename, target, relevant_data): """Writes a renewal config file with the specified name and values. - :param str filename: Absolute path to the config file + :param str o_filename: Absolute path to the previous version of config file + :param str n_filename: Absolute path to the new destination of config file :param dict target: Maps ALL_FOUR to their symlink paths :param dict relevant_data: Renewal configuration options to save @@ -61,21 +62,27 @@ def write_renewal_config(filename, target, relevant_data): :rtype: configobj.ConfigObj """ - # create_empty creates a new config file if filename does not exist - config = configobj.ConfigObj(filename, create_empty=True) + config = configobj.ConfigObj(o_filename) for kind in ALL_FOUR: config[kind] = target[kind] - if relevant_data: - config["renewalparams"] = relevant_data + if "renewalparams" not in config: + config["renewalparams"] = {} config.comments["renewalparams"] = ["", "Options used in " "the renewal process"] + config["renewalparams"].update(relevant_data) + + for k in config["renewalparams"].keys(): + if k not in relevant_data: + del config["renewalparams"][k] + # TODO: add human-readable comments explaining other available # parameters - logger.debug("Writing new config %s.", filename) - config.write() + logger.debug("Writing new config %s.", n_filename) + with open(n_filename, "w") as f: + config.write(outfile=f) return config @@ -101,7 +108,7 @@ def update_configuration(lineagename, target, cli_config): # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) - write_renewal_config(temp_filename, target, values) + write_renewal_config(config_filename, temp_filename, target, values) os.rename(temp_filename, config_filename) return configobj.ConfigObj(config_filename) @@ -130,8 +137,8 @@ def _relevant(option): :rtype: bool """ # The list() here produces a list of the plugin names as strings. - from letsencrypt import renewal - from letsencrypt.plugins import disco as plugins_disco + from certbot import renewal + from certbot.plugins import disco as plugins_disco plugins = list(plugins_disco.PluginsRegistry.find_all()) return (option in renewal.STR_CONFIG_ITEMS or option in renewal.INT_CONFIG_ITEMS @@ -146,7 +153,7 @@ def relevant_values(all_values): :returns: A new dictionary containing items that can be used in renewal. :rtype dict:""" - from letsencrypt import cli + from certbot import cli def _is_cli_default(option, value): # Look through the CLI parser defaults and see if this option is @@ -179,9 +186,9 @@ def relevant_values(all_values): class RenewableCert(object): # pylint: disable=too-many-instance-attributes """Renewable certificate. - Represents a lineage of certificates that is under the management - of the Let's Encrypt client, indicated by the existence of an - associated renewal configuration file. + Represents a lineage of certificates that is under the management of + Certbot, indicated by the existence of an associated renewal + configuration file. Note that the notion of "current version" for a lineage is maintained on disk in the structure of symbolic links, and is not @@ -252,6 +259,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self.privkey = self.configuration["privkey"] self.chain = self.configuration["chain"] self.fullchain = self.configuration["fullchain"] + self.live_dir = os.path.dirname(self.cert) self._fix_symlinks() self._check_symlinks() @@ -798,7 +806,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) - new_config = write_renewal_config(config_filename, target, values) + new_config = write_renewal_config(config_filename, config_filename, target, values) return cls(new_config.filename, cli_config) def save_successor(self, prior_version, new_cert, diff --git a/certbot/tests/__init__.py b/certbot/tests/__init__.py new file mode 100644 index 000000000..2f4d6e07c --- /dev/null +++ b/certbot/tests/__init__.py @@ -0,0 +1 @@ +"""Certbot Tests""" diff --git a/letsencrypt/tests/account_test.py b/certbot/tests/account_test.py similarity index 82% rename from letsencrypt/tests/account_test.py rename to certbot/tests/account_test.py index 9452a74f3..a96e57507 100644 --- a/letsencrypt/tests/account_test.py +++ b/certbot/tests/account_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.account.""" +"""Tests for certbot.account.""" import datetime import os import shutil @@ -12,29 +12,29 @@ import pytz from acme import jose from acme import messages -from letsencrypt import errors +from certbot import errors -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key_2.pem")) class AccountTest(unittest.TestCase): - """Tests for letsencrypt.account.Account.""" + """Tests for certbot.account.Account.""" def setUp(self): - from letsencrypt.account import Account + from certbot.account import Account self.regr = mock.MagicMock() self.meta = Account.Meta( - creation_host="test.letsencrypt.org", + creation_host="test.certbot.org", creation_dt=datetime.datetime( 2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC)) self.acc = Account(self.regr, KEY, self.meta) - with mock.patch("letsencrypt.account.socket") as mock_socket: - mock_socket.getfqdn.return_value = "test.letsencrypt.org" - with mock.patch("letsencrypt.account.datetime") as mock_dt: + with mock.patch("certbot.account.socket") as mock_socket: + mock_socket.getfqdn.return_value = "test.certbot.org" + with mock.patch("certbot.account.datetime") as mock_dt: mock_dt.datetime.now.return_value = self.meta.creation_dt self.acc_no_meta = Account(self.regr, KEY) @@ -49,7 +49,7 @@ class AccountTest(unittest.TestCase): def test_slug(self): self.assertEqual( - self.acc.slug, "test.letsencrypt.org@2015-07-04T14:04:10Z (bca5)") + self.acc.slug, "test.certbot.org@2015-07-04T14:04:10Z (bca5)") def test_repr(self): self.assertEqual( @@ -58,7 +58,7 @@ class AccountTest(unittest.TestCase): class ReportNewAccountTest(unittest.TestCase): - """Tests for letsencrypt.account.report_new_account.""" + """Tests for certbot.account.report_new_account.""" def setUp(self): self.config = mock.MagicMock(config_dir="/etc/letsencrypt") @@ -67,15 +67,15 @@ class ReportNewAccountTest(unittest.TestCase): uri=None, new_authzr_uri=None, body=reg)) def _call(self): - from letsencrypt.account import report_new_account + from certbot.account import report_new_account report_new_account(self.acc, self.config) - @mock.patch("letsencrypt.account.zope.component.queryUtility") + @mock.patch("certbot.account.zope.component.queryUtility") def test_no_reporter(self, mock_zope): mock_zope.return_value = None self._call() - @mock.patch("letsencrypt.account.zope.component.queryUtility") + @mock.patch("certbot.account.zope.component.queryUtility") def test_it(self, mock_zope): self._call() call_list = mock_zope().add_message.call_args_list @@ -85,10 +85,10 @@ class ReportNewAccountTest(unittest.TestCase): class AccountMemoryStorageTest(unittest.TestCase): - """Tests for letsencrypt.account.AccountMemoryStorage.""" + """Tests for certbot.account.AccountMemoryStorage.""" def setUp(self): - from letsencrypt.account import AccountMemoryStorage + from certbot.account import AccountMemoryStorage self.storage = AccountMemoryStorage() def test_it(self): @@ -103,16 +103,16 @@ class AccountMemoryStorageTest(unittest.TestCase): class AccountFileStorageTest(unittest.TestCase): - """Tests for letsencrypt.account.AccountFileStorage.""" + """Tests for certbot.account.AccountFileStorage.""" def setUp(self): self.tmp = tempfile.mkdtemp() self.config = mock.MagicMock( accounts_dir=os.path.join(self.tmp, "accounts")) - from letsencrypt.account import AccountFileStorage + from certbot.account import AccountFileStorage self.storage = AccountFileStorage(self.config) - from letsencrypt.account import Account + from certbot.account import Account self.acc = Account( regr=messages.RegistrationResource( uri=None, new_authzr_uri=None, body=messages.Registration()), @@ -151,7 +151,7 @@ class AccountFileStorageTest(unittest.TestCase): def test_find_all_load_skips(self): self.storage.load = mock.MagicMock( side_effect=["x", errors.AccountStorageError, "z"]) - with mock.patch("letsencrypt.account.os.listdir") as mock_listdir: + with mock.patch("certbot.account.os.listdir") as mock_listdir: mock_listdir.return_value = ["x", "y", "z"] self.assertEqual(["x", "z"], self.storage.find_all()) diff --git a/letsencrypt/tests/acme_util.py b/certbot/tests/acme_util.py similarity index 98% rename from letsencrypt/tests/acme_util.py rename to certbot/tests/acme_util.py index ea5438923..3d33c5723 100644 --- a/letsencrypt/tests/acme_util.py +++ b/certbot/tests/acme_util.py @@ -6,7 +6,7 @@ from acme import challenges from acme import jose from acme import messages -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = test_util.load_rsa_private_key('rsa512_key.pem') diff --git a/letsencrypt/tests/auth_handler_test.py b/certbot/tests/auth_handler_test.py similarity index 90% rename from letsencrypt/tests/auth_handler_test.py rename to certbot/tests/auth_handler_test.py index b7ac04984..3facd4f7c 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/certbot/tests/auth_handler_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.auth_handler.""" +"""Tests for certbot.auth_handler.""" import functools import logging import unittest @@ -9,18 +9,18 @@ from acme import challenges from acme import client as acme_client from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt import le_util +from certbot import achallenges +from certbot import errors +from certbot import le_util -from letsencrypt.tests import acme_util +from certbot.tests import acme_util class ChallengeFactoryTest(unittest.TestCase): # pylint: disable=protected-access def setUp(self): - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import AuthHandler # Account is mocked... self.handler = AuthHandler(None, None, mock.Mock(key="mock_key")) @@ -61,7 +61,7 @@ class GetAuthorizationsTest(unittest.TestCase): """ def setUp(self): - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import AuthHandler self.mock_auth = mock.MagicMock(name="ApacheConfigurator") @@ -80,7 +80,7 @@ class GetAuthorizationsTest(unittest.TestCase): def tearDown(self): logging.disable(logging.NOTSET) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name1_tls_sni_01_1(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES) @@ -103,7 +103,7 @@ class GetAuthorizationsTest(unittest.TestCase): self.assertEqual(len(authzr), 1) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name1_tls_sni_01_1_http_01_1_dns_1(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES, combos=False) @@ -129,7 +129,7 @@ class GetAuthorizationsTest(unittest.TestCase): # Length of authorizations list self.assertEqual(len(authzr), 1) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name3_tls_sni_01_3(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES) @@ -182,8 +182,8 @@ class PollChallengesTest(unittest.TestCase): """Test poll challenges.""" def setUp(self): - from letsencrypt.auth_handler import challb_to_achall - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import challb_to_achall + from certbot.auth_handler import AuthHandler # Account and network are mocked... self.mock_net = mock.MagicMock() @@ -210,7 +210,7 @@ class PollChallengesTest(unittest.TestCase): challb_to_achall(challb, mock.Mock(key="dummy_key"), dom) for challb in self.handler.authzr[dom].body.challenges] - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_poll_challenges(self, unused_mock_time): self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid self.handler._poll_challenges(self.chall_update, False) @@ -218,7 +218,7 @@ class PollChallengesTest(unittest.TestCase): for authzr in self.handler.authzr.values(): self.assertEqual(authzr.body.status, messages.STATUS_VALID) - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_poll_challenges_failure_best_effort(self, unused_mock_time): self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid self.handler._poll_challenges(self.chall_update, True) @@ -226,17 +226,17 @@ class PollChallengesTest(unittest.TestCase): for authzr in self.handler.authzr.values(): self.assertEqual(authzr.body.status, messages.STATUS_PENDING) - @mock.patch("letsencrypt.auth_handler.time") - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.time") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_poll_challenges_failure(self, unused_mock_time, unused_mock_zope): self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid self.assertRaises( errors.AuthorizationError, self.handler._poll_challenges, self.chall_update, False) - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_unable_to_find_challenge_status(self, unused_mock_time): - from letsencrypt.auth_handler import challb_to_achall + from certbot.auth_handler import challb_to_achall self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid self.chall_update[self.doms[0]].append( challb_to_achall(acme_util.DNS_P, "key", self.doms[0])) @@ -295,10 +295,10 @@ class PollChallengesTest(unittest.TestCase): class ChallbToAchallTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler.challb_to_achall.""" + """Tests for certbot.auth_handler.challb_to_achall.""" def _call(self, challb): - from letsencrypt.auth_handler import challb_to_achall + from certbot.auth_handler import challb_to_achall return challb_to_achall(challb, "account_key", "domain") def test_it(self): @@ -311,7 +311,7 @@ class ChallbToAchallTest(unittest.TestCase): class GenChallengePathTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler.gen_challenge_path. + """Tests for certbot.auth_handler.gen_challenge_path. .. todo:: Add more tests for dumb_path... depending on what we want to do. @@ -324,7 +324,7 @@ class GenChallengePathTest(unittest.TestCase): @classmethod def _call(cls, challbs, preferences, combinations): - from letsencrypt.auth_handler import gen_challenge_path + from certbot.auth_handler import gen_challenge_path return gen_challenge_path(challbs, preferences, combinations) def test_common_case(self): @@ -354,7 +354,7 @@ class GenChallengePathTest(unittest.TestCase): class ReportFailedChallsTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler._report_failed_challs.""" + """Tests for certbot.auth_handler._report_failed_challs.""" # pylint: disable=protected-access def setUp(self): @@ -388,18 +388,18 @@ class ReportFailedChallsTest(unittest.TestCase): domain="foo.bar", account_key="key") - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_same_error_and_domain(self, mock_zope): - from letsencrypt import auth_handler + from certbot import auth_handler auth_handler._report_failed_challs([self.http01, self.tls_sni_same]) call_list = mock_zope().add_message.call_args_list self.assertTrue(len(call_list) == 1) self.assertTrue("Domain: example.com\nType: tls\nDetail: detail" in call_list[0][0][0]) - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_different_errors_and_domains(self, mock_zope): - from letsencrypt import auth_handler + from certbot import auth_handler auth_handler._report_failed_challs([self.http01, self.tls_sni_diff]) self.assertTrue(mock_zope().add_message.call_count == 2) diff --git a/letsencrypt/tests/cli_test.py b/certbot/tests/cli_test.py similarity index 78% rename from letsencrypt/tests/cli_test.py rename to certbot/tests/cli_test.py index 04b5a2f3c..31056cafe 100644 --- a/letsencrypt/tests/cli_test.py +++ b/certbot/tests/cli_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.cli.""" +"""Tests for certbot.cli.""" from __future__ import print_function import argparse @@ -12,25 +12,26 @@ import unittest import mock import six +from six.moves import reload_module # pylint: disable=import-error from acme import jose -from letsencrypt import account -from letsencrypt import cli -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import le_util -from letsencrypt import main -from letsencrypt import renewal -from letsencrypt import storage +from certbot import account +from certbot import cli +from certbot import configuration +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import le_util +from certbot import main +from certbot import renewal +from certbot import storage -from letsencrypt.plugins import disco -from letsencrypt.plugins import manual +from certbot.plugins import disco +from certbot.plugins import manual -from letsencrypt.tests import storage_test -from letsencrypt.tests import test_util +from certbot.tests import storage_test +from certbot.tests import test_util CERT = test_util.vector_path('cert.pem') @@ -56,33 +57,24 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # pylint: disable=protected-access cli._parser = cli.set_by_cli.detector = None - def _call(self, args): + def _call(self, args, stdout=None): "Run the cli with output streams and actual client mocked out" - with mock.patch('letsencrypt.main.client') as client: - ret, stdout, stderr = self._call_no_clientmock(args) + with mock.patch('certbot.main.client') as client: + ret, stdout, stderr = self._call_no_clientmock(args, stdout) return ret, stdout, stderr, client - def _call_no_clientmock(self, args): + def _call_no_clientmock(self, args, stdout=None): "Run the client with output streams mocked out" args = self.standard_args + args - with mock.patch('letsencrypt.main.sys.stdout') as stdout: - with mock.patch('letsencrypt.main.sys.stderr') as stderr: - ret = main.main(args[:]) # NOTE: parser can alter its args! - return ret, stdout, stderr - def _call_stdout(self, args): - """ - Variant of _call that preserves stdout so that it can be mocked by the - caller. - """ - args = self.standard_args + args - with mock.patch('letsencrypt.main.sys.stderr') as stderr: - with mock.patch('letsencrypt.main.client') as client: + toy_stdout = stdout if stdout else six.StringIO() + with mock.patch('certbot.main.sys.stdout', new=toy_stdout): + with mock.patch('certbot.main.sys.stderr') as stderr: ret = main.main(args[:]) # NOTE: parser can alter its args! - return ret, None, stderr, client + return ret, toy_stdout, stderr def test_no_flags(self): - with mock.patch('letsencrypt.main.run') as mock_run: + with mock.patch('certbot.main.run') as mock_run: self._call([]) self.assertEqual(1, mock_run.call_count) @@ -90,10 +82,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Run a command, and return the ouput string for scrutiny" output = six.StringIO() - with mock.patch('letsencrypt.main.sys.stdout', new=output): - self.assertRaises(SystemExit, self._call_stdout, args) - out = output.getvalue() - return out + self.assertRaises(SystemExit, self._call, args, output) + out = output.getvalue() + return out def test_help(self): self.assertRaises(SystemExit, self._call, ['--help']) @@ -142,7 +133,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Ensure that a particular error raises a missing cli flag error containing message" exc = None try: - with mock.patch('letsencrypt.main.sys.stderr'): + with mock.patch('certbot.main.sys.stderr'): main.main(self.standard_args + args[:]) # NOTE: parser can alter its args! except errors.MissingCommandlineFlag as exc: self.assertTrue(message in str(exc)) @@ -153,15 +144,15 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._cli_missing_flag(args, "specify a plugin") args.extend(['--standalone', '-d', 'eg.is']) self._cli_missing_flag(args, "register before running") - with mock.patch('letsencrypt.main._auth_from_domains'): - with mock.patch('letsencrypt.main.client.acme_from_config_key'): + with mock.patch('certbot.main._auth_from_domains'): + with mock.patch('certbot.main.client.acme_from_config_key'): args.extend(['--email', 'io@io.is']) self._cli_missing_flag(args, "--agree-tos") - @mock.patch('letsencrypt.main.client.acme_client.Client') - @mock.patch('letsencrypt.main._determine_account') - @mock.patch('letsencrypt.main.client.Client.obtain_and_enroll_certificate') - @mock.patch('letsencrypt.main._auth_from_domains') + @mock.patch('certbot.main.client.acme_client.Client') + @mock.patch('certbot.main._determine_account') + @mock.patch('certbot.main.client.Client.obtain_and_enroll_certificate') + @mock.patch('certbot.main._auth_from_domains') def test_user_agent(self, afd, _obt, det, _client): # Normally the client is totally mocked out, but here we need more # arguments to automate it... @@ -170,7 +161,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods det.return_value = mock.MagicMock(), None afd.return_value = mock.MagicMock(), "newcert" - with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('certbot.main.client.acme_client.ClientNetwork') as acme_net: self._call_no_clientmock(args) os_ver = " ".join(le_util.get_os_info()) ua = acme_net.call_args[1]["user_agent"] @@ -180,7 +171,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if "linux" in plat.lower(): self.assertTrue(platform.linux_distribution()[0] in ua) - with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('certbot.main.client.acme_client.ClientNetwork') as acme_net: ua = "bandersnatch" args += ["--user-agent", ua] self._call_no_clientmock(args) @@ -192,7 +183,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with mock.patch('letsencrypt.main.install') as mock_install: + with mock.patch('certbot.main.install') as mock_install: self._call(['install', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -203,14 +194,14 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(args.chain_path, os.path.abspath(chain)) self.assertEqual(args.fullchain_path, os.path.abspath(fullchain)) - @mock.patch('letsencrypt.main.plug_sel.record_chosen_plugins') - @mock.patch('letsencrypt.main.plug_sel.pick_installer') + @mock.patch('certbot.main.plug_sel.record_chosen_plugins') + @mock.patch('certbot.main.plug_sel.pick_installer') def test_installer_selection(self, mock_pick_installer, _rec): self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert', '--key-path', 'key', '--chain-path', 'chain']) self.assertEqual(mock_pick_installer.call_count, 1) - @mock.patch('letsencrypt.le_util.exe_exists') + @mock.patch('certbot.le_util.exe_exists') def test_configurator_selection(self, mock_exe_exists): mock_exe_exists.return_value = True real_plugins = disco.PluginsRegistry.find_all() @@ -219,7 +210,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # This needed two calls to find_all(), which we're avoiding for now # because of possible side effects: # https://github.com/letsencrypt/letsencrypt/commit/51ed2b681f87b1eb29088dd48718a54f401e4855 - #with mock.patch('letsencrypt.cli.plugins_testable') as plugins: + #with mock.patch('certbot.cli.plugins_testable') as plugins: # plugins.return_value = {"apache": True, "nginx": True} # ret, _, _, _ = self._call(args) # self.assertTrue("Too many flags setting" in ret) @@ -229,28 +220,21 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "example.com", "--debug"] if "nginx" in real_plugins: # Sending nginx a non-existent conf dir will simulate misconfiguration - # (we can only do that if letsencrypt-nginx is actually present) + # (we can only do that if certbot-nginx is actually present) ret, _, _, _ = self._call(args) self.assertTrue("The nginx plugin is not working" in ret) self.assertTrue("MisconfigurationError" in ret) - args = ["certonly", "--webroot"] - try: - self._call(args) - assert False, "Exception should have been raised" - except errors.PluginSelectionError as e: - self.assertTrue("please set either --webroot-path" in e.message) - self._cli_missing_flag(["--standalone"], "With the standalone plugin, you probably") - with mock.patch("letsencrypt.main._init_le_client") as mock_init: - with mock.patch("letsencrypt.main._auth_from_domains") as mock_afd: + with mock.patch("certbot.main._init_le_client") as mock_init: + with mock.patch("certbot.main._auth_from_domains") as mock_afd: mock_afd.return_value = (mock.MagicMock(), mock.MagicMock()) self._call(["certonly", "--manual", "-d", "foo.bar"]) unused_config, auth, unused_installer = mock_init.call_args[0] self.assertTrue(isinstance(auth, manual.Authenticator)) - with mock.patch('letsencrypt.main.obtain_cert') as mock_certonly: + with mock.patch('certbot.main.obtain_cert') as mock_certonly: self._call(["auth", "--standalone"]) self.assertEqual(1, mock_certonly.call_count) @@ -273,8 +257,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods for r in xrange(len(flags)))): self._call(['plugins'] + list(args)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_no_args(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -283,10 +267,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods plugins.visible.assert_called_once_with() plugins.visible().ifaces.assert_called_once_with(ifaces) filtered = plugins.visible().ifaces() - stdout.write.called_once_with(str(filtered)) + self.assertEqual(stdout.getvalue().strip(), str(filtered)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_init(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -298,10 +282,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(filtered.init.call_count, 1) filtered.verify.assert_called_once_with(ifaces) verified = filtered.verify() - stdout.write.called_once_with(str(verified)) + self.assertEqual(stdout.getvalue().strip(), str(verified)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_prepare(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -315,7 +299,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods verified.prepare.assert_called_once_with() verified.available.assert_called_once_with() available = verified.available() - stdout.write.called_once_with(str(available)) + self.assertEqual(stdout.getvalue().strip(), str(available)) def test_certonly_abspath(self): cert = 'cert' @@ -323,7 +307,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtaincert: + with mock.patch('certbot.main.obtain_cert') as mock_obtaincert: self._call(['certonly', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -373,7 +357,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods try: self._call(['--csr', CSR]) except errors.Error as e: - assert "Please try the certonly" in e.message + assert "Please try the certonly" in repr(e) return assert False, "Expected supplying --csr to fail with default verb" @@ -436,20 +420,40 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods short_args += '--server example.com'.split() self._check_server_conflict_message(short_args, '--staging') - def _assert_dry_run_flag_worked(self, namespace): + def _assert_dry_run_flag_worked(self, namespace, existing_account): self.assertTrue(namespace.dry_run) self.assertTrue(namespace.break_my_certs) self.assertTrue(namespace.staging) self.assertEqual(namespace.server, constants.STAGING_URI) + if existing_account: + self.assertTrue(namespace.tos) + self.assertTrue(namespace.register_unsafely_without_email) + else: + self.assertFalse(namespace.tos) + self.assertFalse(namespace.register_unsafely_without_email) + def test_dry_run_flag(self): parse = self._get_argument_parser() - short_args = ['--dry-run'] + config_dir = tempfile.mkdtemp() + short_args = '--dry-run --config-dir {0}'.format(config_dir).split() self.assertRaises(errors.Error, parse, short_args) - self._assert_dry_run_flag_worked(parse(short_args + ['auth'])) + self._assert_dry_run_flag_worked( + parse(short_args + ['auth']), False) + self._assert_dry_run_flag_worked( + parse(short_args + ['certonly']), False) + self._assert_dry_run_flag_worked( + parse(short_args + ['renew']), False) + + account_dir = os.path.join(config_dir, constants.ACCOUNTS_DIR) + os.mkdir(account_dir) + os.mkdir(os.path.join(account_dir, 'fake_account_dir')) + + self._assert_dry_run_flag_worked(parse(short_args + ['auth']), True) + self._assert_dry_run_flag_worked(parse(short_args + ['renew']), True) short_args += ['certonly'] - self._assert_dry_run_flag_worked(parse(short_args)) + self._assert_dry_run_flag_worked(parse(short_args), True) short_args += '--server example.com'.split() conflicts = ['--dry-run'] @@ -459,71 +463,17 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods conflicts += ['--staging'] self._check_server_conflict_message(short_args, conflicts) - def _webroot_map_test(self, map_arg, path_arg, domains_arg, # pylint: disable=too-many-arguments - expected_map, expectect_domains, extra_args=None): - parse = self._get_argument_parser() - webroot_map_args = extra_args if extra_args else [] - if map_arg: - webroot_map_args.extend(["--webroot-map", map_arg]) - if path_arg: - webroot_map_args.extend(["-w", path_arg]) - if domains_arg: - webroot_map_args.extend(["-d", domains_arg]) - namespace = parse(webroot_map_args) - domains = main._find_domains(namespace, mock.MagicMock()) # pylint: disable=protected-access - self.assertEqual(namespace.webroot_map, expected_map) - self.assertEqual(set(domains), set(expectect_domains)) - - def test_parse_webroot(self): - parse = self._get_argument_parser() - webroot_args = ['--webroot', '-w', '/var/www/example', - '-d', 'example.com,www.example.com', '-w', '/var/www/superfluous', - '-d', 'superfluo.us', '-d', 'www.superfluo.us'] - namespace = parse(webroot_args) - self.assertEqual(namespace.webroot_map, { - 'example.com': '/var/www/example', - 'www.example.com': '/var/www/example', - 'www.superfluo.us': '/var/www/superfluous', - 'superfluo.us': '/var/www/superfluous'}) - - webroot_args = ['-d', 'stray.example.com'] + webroot_args - self.assertRaises(errors.Error, parse, webroot_args) - - simple_map = '{"eg.com" : "/tmp"}' - expected_map = {"eg.com": "/tmp"} - self._webroot_map_test(simple_map, None, None, expected_map, ["eg.com"]) - - # test merging webroot maps from the cli and a webroot map - expected_map["eg2.com"] = "/tmp2" - domains = ["eg.com", "eg2.com"] - self._webroot_map_test(simple_map, "/tmp2", "eg2.com,eg.com", expected_map, domains) - - # test inclusion of interactively specified domains in the webroot map - with mock.patch('letsencrypt.display.ops.choose_names') as mock_choose: - mock_choose.return_value = domains - expected_map["eg2.com"] = "/tmp" - self._webroot_map_test(None, "/tmp", None, expected_map, domains) - - extra_args = ['-c', test_util.vector_path('webrootconftest.ini')] - self._webroot_map_test(None, None, None, expected_map, domains, extra_args) - - webroot_map_args = ['--webroot-map', - '{"eg.com.,www.eg.com": "/tmp", "eg.is.": "/tmp2"}'] - namespace = parse(webroot_map_args) - self.assertEqual(namespace.webroot_map, - {"eg.com": "/tmp", "www.eg.com": "/tmp", "eg.is": "/tmp2"}) - def _certonly_new_request_common(self, mock_client, args=None): - with mock.patch('letsencrypt.main._treat_as_renewal') as mock_renewal: + with mock.patch('certbot.main._treat_as_renewal') as mock_renewal: mock_renewal.return_value = ("newcert", None) - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client if args is None: args = [] args += '-d foo.bar -a standalone certonly'.split() self._call(args) - @mock.patch('letsencrypt.main.zope.component.getUtility') + @mock.patch('certbot.main.zope.component.getUtility') def test_certonly_dry_run_new_request_success(self, mock_get_utility): mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = None @@ -535,8 +485,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # Asserts we don't suggest donating after a successful dry run self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch('letsencrypt.crypto_util.notAfter') - @mock.patch('letsencrypt.main.zope.component.getUtility') + @mock.patch('certbot.crypto_util.notAfter') + @mock.patch('certbot.main.zope.component.getUtility') def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter): cert_path = '/etc/letsencrypt/live/foo.bar' date = '1970-01-01' @@ -563,33 +513,34 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None, args=None, should_renew=True, error_expected=False): # pylint: disable=too-many-locals,too-many-arguments - cert_path = 'letsencrypt/tests/testdata/cert.pem' + cert_path = 'certbot/tests/testdata/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path) mock_lineage.should_autorenew.return_value = due_for_renewal mock_certr = mock.MagicMock() mock_key = mock.MagicMock(pem='pem_key') mock_client = mock.MagicMock() + stdout = None mock_client.obtain_certificate.return_value = (mock_certr, 'chain', mock_key, 'csr') try: - with mock.patch('letsencrypt.main._find_duplicative_certs') as mock_fdc: + with mock.patch('certbot.main._find_duplicative_certs') as mock_fdc: mock_fdc.return_value = (mock_lineage, None) - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.main.zope.component.getUtility' + get_utility_path = 'certbot.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: - with mock.patch('letsencrypt.main.OpenSSL') as mock_ssl: + with mock.patch('certbot.main.renewal.OpenSSL') as mock_ssl: mock_latest = mock.MagicMock() mock_latest.get_issuer.return_value = "Fake fake" mock_ssl.crypto.load_certificate.return_value = mock_latest - with mock.patch('letsencrypt.main.crypto_util'): + with mock.patch('certbot.main.renewal.crypto_util'): if not args: args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly'] if extra_args: args += extra_args try: - ret, _, _, _ = self._call(args) + ret, stdout, _, _ = self._call(args) if ret: print("Returned", ret) raise AssertionError(ret) @@ -612,10 +563,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: self.assertTrue(log_out in lf.read()) - return mock_lineage, mock_get_utility + return mock_lineage, mock_get_utility, stdout def test_certonly_renewal(self): - lineage, get_utility = self._test_renewal_common(True, []) + lineage, get_utility, _ = self._test_renewal_common(True, []) self.assertEqual(lineage.save_successor.call_count, 1) lineage.update_all_links_to.assert_called_once_with( lineage.latest_common_version()) @@ -625,17 +576,18 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_certonly_renewal_triggers(self): # --dry-run should force renewal - _, get_utility = self._test_renewal_common(False, ['--dry-run', '--keep'], - log_out="simulating renewal") + _, get_utility, _ = self._test_renewal_common(False, ['--dry-run', '--keep'], + log_out="simulating renewal") self.assertEqual(get_utility().add_message.call_count, 1) self.assertTrue('dry run' in get_utility().add_message.call_args[0][0]) - _, _ = self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'], - log_out="Auto-renewal forced") + self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'], + log_out="Auto-renewal forced") self.assertEqual(get_utility().add_message.call_count, 1) - _, _ = self._test_renewal_common(False, ['-tvv', '--debug', '--keep'], - log_out="not yet due", should_renew=False) + self._test_renewal_common(False, ['-tvv', '--debug', '--keep'], + log_out="not yet due", should_renew=False) + def _dump_log(self): with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: @@ -660,7 +612,20 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = ["renew", "--dry-run", "-tvv"] self._test_renewal_common(True, [], args=args, should_renew=True) - @mock.patch("letsencrypt.cli.set_by_cli") + def test_quiet_renew(self): + self._make_test_renewal_conf('sample-renewal.conf') + args = ["renew", "--dry-run"] + _, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True) + out = stdout.getvalue() + self.assertTrue("renew" in out) + + args = ["renew", "--dry-run", "-q"] + _, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True) + out = stdout.getvalue() + self.assertEqual("", out) + + + @mock.patch("certbot.cli.set_by_cli") def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf') @@ -691,7 +656,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _test_renew_common(self, renewalparams=None, names=None, assert_oc_called=None, **kwargs): self._make_dummy_renewal_config() - with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: + with mock.patch('certbot.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() mock_lineage.fullchain = "somepath/fullchain.pem" if renewalparams is not None: @@ -699,7 +664,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if names is not None: mock_lineage.names.return_value = names mock_rc.return_value = mock_lineage - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: + with mock.patch('certbot.main.obtain_cert') as mock_obtain_cert: kwargs.setdefault('args', ['renew']) self._test_renewal_common(True, None, should_renew=False, **kwargs) @@ -749,19 +714,19 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_reconstitute_error(self): # pylint: disable=protected-access - with mock.patch('letsencrypt.main.renewal._reconstitute') as mock_reconstitute: + with mock.patch('certbot.main.renewal._reconstitute') as mock_reconstitute: mock_reconstitute.side_effect = Exception self._test_renew_common(assert_oc_called=False, error_expected=True) def test_renew_obtain_cert_error(self): self._make_dummy_renewal_config() - with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: + with mock.patch('certbot.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() mock_lineage.fullchain = "somewhere/fullchain.pem" mock_rc.return_value = mock_lineage mock_lineage.configuration = { 'renewalparams': {'authenticator': 'webroot'}} - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: + with mock.patch('certbot.main.obtain_cert') as mock_obtain_cert: mock_obtain_cert.side_effect = Exception self._test_renewal_common(True, None, error_expected=True, args=['renew'], should_renew=False) @@ -772,9 +737,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._test_renewal_common(True, None, args='renew --csr {0}'.format(CSR).split(), should_renew=False, error_expected=True) - @mock.patch('letsencrypt.main.zope.component.getUtility') - @mock.patch('letsencrypt.main._treat_as_renewal') - @mock.patch('letsencrypt.main._init_le_client') + @mock.patch('certbot.main.zope.component.getUtility') + @mock.patch('certbot.main._treat_as_renewal') + @mock.patch('certbot.main._init_le_client') def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility): mock_renewal.return_value = ('reinstall', mock.MagicMock()) mock_init.return_value = mock_client = mock.MagicMock() @@ -791,9 +756,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_client.obtain_certificate_from_csr.return_value = (certr, chain) cert_path = '/etc/letsencrypt/live/example.com/cert.pem' mock_client.save_certificate.return_value = cert_path, None, None - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.main.zope.component.getUtility' + get_utility_path = 'certbot.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: chain_path = '/etc/letsencrypt/live/example.com/chain.pem' full_path = '/etc/letsencrypt/live/example.com/fullchain.pem' @@ -802,7 +767,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods CSR, cert_path, chain_path, full_path).split() if extra_args: args += extra_args - with mock.patch('letsencrypt.main.crypto_util'): + with mock.patch('certbot.main.crypto_util'): self._call(args) if '--dry-run' in args: @@ -826,7 +791,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue( 'dry run' in mock_get_utility().add_message.call_args[0][0]) - @mock.patch('letsencrypt.main.client.acme_client') + @mock.patch('certbot.main.client.acme_client') def test_revoke_with_key(self, mock_acme_client): server = 'foo.bar' self._call_no_clientmock(['--cert-path', CERT, '--key-path', KEY, @@ -839,7 +804,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = mock_acme_client.Client().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.main._determine_account') + @mock.patch('certbot.main._determine_account') def test_revoke_without_key(self, mock_determine_account): mock_determine_account.return_value = (mock.MagicMock(), None) _, _, _, client = self._call(['--cert-path', CERT, 'revoke']) @@ -848,7 +813,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = client.acme_from_config_key().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.main.sys') + @mock.patch('certbot.main.sys') def test_handle_exception(self, mock_sys): # pylint: disable=protected-access from acme import messages @@ -856,7 +821,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods config = mock.MagicMock() mock_open = mock.mock_open() - with mock.patch('letsencrypt.main.open', mock_open, create=True): + with mock.patch('certbot.main.open', mock_open, create=True): exception = Exception('detail') config.verbose_count = 1 main._handle_exception( @@ -866,7 +831,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods error_msg = mock_sys.exit.call_args_list[0][0][0] self.assertTrue('unexpected error' in error_msg) - with mock.patch('letsencrypt.main.open', mock_open, create=True): + with mock.patch('certbot.main.open', mock_open, create=True): mock_open.side_effect = [KeyboardInterrupt] error = errors.Error('detail') main._handle_exception( @@ -913,13 +878,13 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(contents, test_contents) def test_agree_dev_preview_config(self): - with mock.patch('letsencrypt.main.run') as mocked_run: + with mock.patch('certbot.main.run') as mocked_run: self._call(['-c', test_util.vector_path('cli.ini')]) self.assertTrue(mocked_run.called) class DetermineAccountTest(unittest.TestCase): - """Tests for letsencrypt.cli._determine_account.""" + """Tests for certbot.cli._determine_account.""" def setUp(self): self.args = mock.MagicMock(account=None, email=None, @@ -930,8 +895,8 @@ class DetermineAccountTest(unittest.TestCase): def _call(self): # pylint: disable=protected-access - from letsencrypt.main import _determine_account - with mock.patch('letsencrypt.main.account.AccountFileStorage') as mock_storage: + from certbot.main import _determine_account + with mock.patch('certbot.main.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage return _determine_account(self.config) @@ -948,7 +913,7 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[0].id, self.config.account) self.assertTrue(self.config.email is None) - @mock.patch('letsencrypt.client.display_ops.choose_account') + @mock.patch('certbot.client.display_ops.choose_account') def test_multiple_accounts(self, mock_choose_accounts): for acc in self.accs: self.account_storage.save(acc) @@ -959,11 +924,11 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[1].id, self.config.account) self.assertTrue(self.config.email is None) - @mock.patch('letsencrypt.client.display_ops.get_email') + @mock.patch('certbot.client.display_ops.get_email') def test_no_accounts_no_email(self, mock_get_email): mock_get_email.return_value = 'foo@bar.baz' - with mock.patch('letsencrypt.main.client') as client: + with mock.patch('certbot.main.client') as client: client.register.return_value = ( self.accs[0], mock.sentinel.acme) self.assertEqual((self.accs[0], mock.sentinel.acme), self._call()) @@ -975,7 +940,7 @@ class DetermineAccountTest(unittest.TestCase): def test_no_accounts_email(self): self.config.email = 'other email' - with mock.patch('letsencrypt.main.client') as client: + with mock.patch('certbot.main.client') as client: client.register.return_value = (self.accs[1], mock.sentinel.acme) self._call() self.assertEqual(self.accs[1].id, self.config.account) @@ -993,9 +958,9 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): def tearDown(self): shutil.rmtree(self.tempdir) - @mock.patch('letsencrypt.le_util.make_or_verify_dir') + @mock.patch('certbot.le_util.make_or_verify_dir') def test_find_duplicative_names(self, unused_makedir): - from letsencrypt.main import _find_duplicative_certs + from certbot.main import _find_duplicative_certs test_cert = test_util.load_vector('cert-san.pem') with open(self.test_rc.cert, 'w') as f: f.write(test_cert) @@ -1023,5 +988,79 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): self.assertEqual(result, (None, None)) +class DefaultTest(unittest.TestCase): + """Tests for certbot.cli._Default.""" + + def setUp(self): + # pylint: disable=protected-access + self.default1 = cli._Default() + self.default2 = cli._Default() + + def test_boolean(self): + self.assertFalse(self.default1) + self.assertFalse(self.default2) + + def test_equality(self): + self.assertEqual(self.default1, self.default2) + + def test_hash(self): + self.assertEqual(hash(self.default1), hash(self.default2)) + + +class SetByCliTest(unittest.TestCase): + """Tests for certbot.set_by_cli and related functions.""" + + def setUp(self): + reload_module(cli) + + def test_webroot_map(self): + args = '-w /var/www/html -d example.com'.split() + verb = 'renew' + self.assertTrue(_call_set_by_cli('webroot_map', args, verb)) + + def test_report_config_interaction_str(self): + cli.report_config_interaction('manual_public_ip_logging_ok', + 'manual_test_mode') + cli.report_config_interaction('manual_test_mode', 'manual') + + self._test_report_config_interaction_common() + + def test_report_config_interaction_iterable(self): + cli.report_config_interaction(('manual_public_ip_logging_ok',), + ('manual_test_mode',)) + cli.report_config_interaction(('manual_test_mode',), ('manual',)) + + self._test_report_config_interaction_common() + + def _test_report_config_interaction_common(self): + """Tests implied interaction between manual flags. + + --manual implies --manual-test-mode which implies + --manual-public-ip-logging-ok. These interactions don't actually + exist in the client, but are used here for testing purposes. + + """ + + args = ['--manual'] + verb = 'renew' + for v in ('manual', 'manual_test_mode', 'manual_public_ip_logging_ok'): + self.assertTrue(_call_set_by_cli(v, args, verb)) + + cli.set_by_cli.detector = None + + args = ['--manual-test-mode'] + for v in ('manual_test_mode', 'manual_public_ip_logging_ok'): + self.assertTrue(_call_set_by_cli(v, args, verb)) + + self.assertFalse(_call_set_by_cli('manual', args, verb)) + + +def _call_set_by_cli(var, args, verb): + with mock.patch('certbot.cli.helpful_parser') as mock_parser: + mock_parser.args = args + mock_parser.verb = verb + return cli.set_by_cli(var) + + if __name__ == '__main__': unittest.main() # pragma: no cover diff --git a/letsencrypt/tests/client_test.py b/certbot/tests/client_test.py similarity index 87% rename from letsencrypt/tests/client_test.py rename to certbot/tests/client_test.py index ed4e5def0..a41301148 100644 --- a/letsencrypt/tests/client_test.py +++ b/certbot/tests/client_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.client.""" +"""Tests for certbot.client.""" import os import shutil import tempfile @@ -9,11 +9,11 @@ import mock from acme import jose -from letsencrypt import account -from letsencrypt import errors -from letsencrypt import le_util +from certbot import account +from certbot import errors +from certbot import le_util -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = test_util.load_vector("rsa512_key.pem") @@ -30,7 +30,7 @@ class ConfigHelper(object): self.__dict__.update(kwds) class RegisterTest(unittest.TestCase): - """Tests for letsencrypt.client.register.""" + """Tests for certbot.client.register.""" def setUp(self): self.config = mock.MagicMock(rsa_key_size=1024, register_unsafely_without_email=False) @@ -38,13 +38,13 @@ class RegisterTest(unittest.TestCase): self.tos_cb = mock.MagicMock() def _call(self): - from letsencrypt.client import register + from certbot.client import register return register(self.config, self.account_storage, self.tos_cb) def test_no_tos(self): - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client.register().terms_of_service = "http://tos" - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.account.report_new_account"): self.tos_cb.return_value = False self.assertRaises(errors.Error, self._call) @@ -55,17 +55,17 @@ class RegisterTest(unittest.TestCase): self._call() def test_it(self): - with mock.patch("letsencrypt.client.acme_client.Client"): - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.client.acme_client.Client"): + with mock.patch("certbot.account.report_new_account"): self._call() - @mock.patch("letsencrypt.account.report_new_account") - @mock.patch("letsencrypt.client.display_ops.get_email") + @mock.patch("certbot.account.report_new_account") + @mock.patch("certbot.client.display_ops.get_email") def test_email_retry(self, _rep, mock_get_email): from acme import messages msg = "DNS problem: NXDOMAIN looking up MX for example.com" mx_err = messages.Error(detail=msg, typ="urn:acme:error:invalidEmail") - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client().register.side_effect = [mx_err, mock.MagicMock()] self._call() self.assertEqual(mock_get_email.call_count, 1) @@ -74,12 +74,13 @@ class RegisterTest(unittest.TestCase): self.config.email = None self.assertRaises(errors.Error, self._call) - @mock.patch("letsencrypt.client.logger") + @mock.patch("certbot.client.logger") def test_without_email(self, mock_logger): - with mock.patch("letsencrypt.client.acme_client.Client"): - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.client.acme_client.Client"): + with mock.patch("certbot.account.report_new_account"): self.config.email = None self.config.register_unsafely_without_email = True + self.config.dry_run = False self._call() mock_logger.warn.assert_called_once_with(mock.ANY) @@ -87,12 +88,12 @@ class RegisterTest(unittest.TestCase): from acme import messages msg = "Test" mx_err = messages.Error(detail=msg, typ="malformed", title="title") - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client().register.side_effect = [mx_err, mock.MagicMock()] self.assertRaises(messages.Error, self._call) class ClientTest(unittest.TestCase): - """Tests for letsencrypt.client.Client.""" + """Tests for certbot.client.Client.""" def setUp(self): self.config = mock.MagicMock( @@ -101,8 +102,8 @@ class ClientTest(unittest.TestCase): self.account = mock.MagicMock(**{"key.pem": KEY}) self.eg_domains = ["example.com", "www.example.com"] - from letsencrypt.client import Client - with mock.patch("letsencrypt.client.acme_client.Client") as acme: + from certbot.client import Client + with mock.patch("certbot.client.acme_client.Client") as acme: self.acme_client = acme self.acme = acme.return_value = mock.MagicMock() self.client = Client( @@ -134,25 +135,21 @@ class ClientTest(unittest.TestCase): self.acme.fetch_chain.assert_called_once_with(mock.sentinel.certr) # FIXME move parts of this to test_cli.py... - @mock.patch("letsencrypt.client.logger") - @mock.patch("letsencrypt.cli.process_domain") - def test_obtain_certificate_from_csr(self, mock_process_domain, mock_logger): + @mock.patch("certbot.client.logger") + def test_obtain_certificate_from_csr(self, mock_logger): self._mock_obtain_certificate() - from letsencrypt import cli + from certbot import cli test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN) mock_parsed_args = mock.MagicMock() # The CLI should believe that this is a certonly request, because # a CSR would not be allowed with other kinds of requests! mock_parsed_args.verb = "certonly" - with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR: + with mock.patch("certbot.client.le_util.CSR") as mock_CSR: mock_CSR.return_value = test_csr mock_parsed_args.domains = self.eg_domains[:] mock_parser = mock.MagicMock(cli.HelpfulArgumentParser) cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args) - # make sure cli processing occurred - cli_processed = (call[0][1] for call in mock_process_domain.call_args_list) - self.assertEqual(set(cli_processed), set(("example.com", "www.example.com"))) # Now provoke an inconsistent domains error... mock_parsed_args.domains.append("hippopotamus.io") self.assertRaises(errors.ConfigurationError, @@ -189,7 +186,7 @@ class ClientTest(unittest.TestCase): test_csr) mock_logger.warning.assert_called_once_with(mock.ANY) - @mock.patch("letsencrypt.client.crypto_util") + @mock.patch("certbot.client.crypto_util") def test_obtain_certificate(self, mock_crypto_util): self._mock_obtain_certificate() @@ -293,7 +290,7 @@ class ClientTest(unittest.TestCase): ["foo.bar"], "key", "cert", "chain", "fullchain") installer.recovery_routine.assert_called_once_with() - @mock.patch("letsencrypt.client.zope.component.getUtility") + @mock.patch("certbot.client.zope.component.getUtility") def test_deploy_certificate_restart_failure(self, mock_get_utility): installer = mock.MagicMock() installer.restart.side_effect = [errors.PluginError, None] @@ -305,7 +302,7 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 2) - @mock.patch("letsencrypt.client.zope.component.getUtility") + @mock.patch("certbot.client.zope.component.getUtility") def test_deploy_certificate_restart_failure2(self, mock_get_utility): installer = mock.MagicMock() installer.restart.side_effect = errors.PluginError @@ -318,7 +315,7 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 1) - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config(self, mock_enhancements): config = ConfigHelper(redirect=True, hsts=False, uir=False) self.assertRaises(errors.Error, @@ -334,7 +331,7 @@ class ClientTest(unittest.TestCase): self.assertEqual(installer.save.call_count, 1) installer.restart.assert_called_once_with() - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config_no_ask(self, mock_enhancements): config = ConfigHelper(redirect=True, hsts=False, uir=False) self.assertRaises(errors.Error, @@ -362,7 +359,7 @@ class ClientTest(unittest.TestCase): self.assertEqual(installer.save.call_count, 3) self.assertEqual(installer.restart.call_count, 3) - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config_unsupported(self, mock_enhancements): installer = mock.MagicMock() self.client.installer = installer @@ -378,8 +375,8 @@ class ClientTest(unittest.TestCase): self.assertRaises(errors.Error, self.client.enhance_config, ["foo.bar"], config) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_enhance_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -395,8 +392,8 @@ class ClientTest(unittest.TestCase): installer.recovery_routine.assert_called_once_with() self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_save_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -412,8 +409,8 @@ class ClientTest(unittest.TestCase): installer.recovery_routine.assert_called_once_with() self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_restart_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -431,8 +428,8 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 2) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_restart_failure2(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -452,15 +449,15 @@ class ClientTest(unittest.TestCase): class RollbackTest(unittest.TestCase): - """Tests for letsencrypt.client.rollback.""" + """Tests for certbot.client.rollback.""" def setUp(self): self.m_install = mock.MagicMock() @classmethod def _call(cls, checkpoints, side_effect): - from letsencrypt.client import rollback - with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi: + from certbot.client import rollback + with mock.patch("certbot.client.plugin_selection.pick_installer") as mpi: mpi.side_effect = side_effect rollback(None, checkpoints, {}, mock.MagicMock()) diff --git a/letsencrypt/tests/colored_logging_test.py b/certbot/tests/colored_logging_test.py similarity index 85% rename from letsencrypt/tests/colored_logging_test.py rename to certbot/tests/colored_logging_test.py index 4080157fc..91c6b8c08 100644 --- a/letsencrypt/tests/colored_logging_test.py +++ b/certbot/tests/colored_logging_test.py @@ -1,17 +1,17 @@ -"""Tests for letsencrypt.colored_logging.""" +"""Tests for certbot.colored_logging.""" import logging import unittest import six -from letsencrypt import le_util +from certbot import le_util class StreamHandlerTest(unittest.TestCase): - """Tests for letsencrypt.colored_logging.""" + """Tests for certbot.colored_logging.""" def setUp(self): - from letsencrypt import colored_logging + from certbot import colored_logging self.stream = six.StringIO() self.stream.isatty = lambda: True diff --git a/letsencrypt/tests/configuration_test.py b/certbot/tests/configuration_test.py similarity index 87% rename from letsencrypt/tests/configuration_test.py rename to certbot/tests/configuration_test.py index a4f881d34..13d85bd9f 100644 --- a/letsencrypt/tests/configuration_test.py +++ b/certbot/tests/configuration_test.py @@ -1,26 +1,26 @@ -"""Tests for letsencrypt.configuration.""" +"""Tests for certbot.configuration.""" import os import unittest import mock -from letsencrypt import errors +from certbot import errors class NamespaceConfigTest(unittest.TestCase): - """Tests for letsencrypt.configuration.NamespaceConfig.""" + """Tests for certbot.configuration.NamespaceConfig.""" def setUp(self): self.namespace = mock.MagicMock( config_dir='/tmp/config', work_dir='/tmp/foo', foo='bar', server='https://acme-server.org:443/new', tls_sni_01_port=1234, http01_port=4321) - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig self.config = NamespaceConfig(self.namespace) def test_init_same_ports(self): self.namespace.tls_sni_01_port = 4321 - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig self.assertRaises(errors.Error, NamespaceConfig, self.namespace) def test_proxy_getattr(self): @@ -36,7 +36,7 @@ class NamespaceConfigTest(unittest.TestCase): self.assertEqual(['user:pass@acme.server:443', 'p', 'a', 't', 'h'], self.config.server_path.split(os.path.sep)) - @mock.patch('letsencrypt.configuration.constants') + @mock.patch('certbot.configuration.constants') def test_dynamic_dirs(self, constants): constants.ACCOUNTS_DIR = 'acc' constants.BACKUP_DIR = 'backups' @@ -55,7 +55,7 @@ class NamespaceConfigTest(unittest.TestCase): self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t') def test_absolute_paths(self): - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig config_base = "foo" work_base = "bar" @@ -88,14 +88,14 @@ class NamespaceConfigTest(unittest.TestCase): class RenewerConfigurationTest(unittest.TestCase): - """Test for letsencrypt.configuration.RenewerConfiguration.""" + """Test for certbot.configuration.RenewerConfiguration.""" def setUp(self): self.namespace = mock.MagicMock(config_dir='/tmp/config') - from letsencrypt.configuration import RenewerConfiguration + from certbot.configuration import RenewerConfiguration self.config = RenewerConfiguration(self.namespace) - @mock.patch('letsencrypt.configuration.constants') + @mock.patch('certbot.configuration.constants') def test_dynamic_dirs(self, constants): constants.ARCHIVE_DIR = 'a' constants.LIVE_DIR = 'l' @@ -109,8 +109,8 @@ class RenewerConfigurationTest(unittest.TestCase): self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf') def test_absolute_paths(self): - from letsencrypt.configuration import NamespaceConfig - from letsencrypt.configuration import RenewerConfiguration + from certbot.configuration import NamespaceConfig + from certbot.configuration import RenewerConfiguration config_base = "foo" work_base = "bar" diff --git a/letsencrypt/tests/crypto_util_test.py b/certbot/tests/crypto_util_test.py similarity index 74% rename from letsencrypt/tests/crypto_util_test.py rename to certbot/tests/crypto_util_test.py index 1a9f39572..52e595577 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/certbot/tests/crypto_util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.crypto_util.""" +"""Tests for certbot.crypto_util.""" import logging import shutil import tempfile @@ -8,9 +8,9 @@ import OpenSSL import mock import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.tests import test_util +from certbot import errors +from certbot import interfaces +from certbot.tests import test_util RSA256_KEY = test_util.load_vector('rsa256_key.pem') @@ -21,7 +21,7 @@ SAN_CERT = test_util.load_vector('cert-san.pem') class InitSaveKeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.init_save_key.""" + """Tests for certbot.crypto_util.init_save_key.""" def setUp(self): logging.disable(logging.CRITICAL) zope.component.provideUtility( @@ -34,24 +34,24 @@ class InitSaveKeyTest(unittest.TestCase): @classmethod def _call(cls, key_size, key_dir): - from letsencrypt.crypto_util import init_save_key - return init_save_key(key_size, key_dir, 'key-letsencrypt.pem') + from certbot.crypto_util import init_save_key + return init_save_key(key_size, key_dir, 'key-certbot.pem') - @mock.patch('letsencrypt.crypto_util.make_key') + @mock.patch('certbot.crypto_util.make_key') def test_success(self, mock_make): mock_make.return_value = 'key_pem' key = self._call(1024, self.key_dir) self.assertEqual(key.pem, 'key_pem') - self.assertTrue('key-letsencrypt.pem' in key.file) + self.assertTrue('key-certbot.pem' in key.file) - @mock.patch('letsencrypt.crypto_util.make_key') + @mock.patch('certbot.crypto_util.make_key') def test_key_failure(self, mock_make): mock_make.side_effect = ValueError self.assertRaises(ValueError, self._call, 431, self.key_dir) class InitSaveCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.init_save_csr.""" + """Tests for certbot.crypto_util.init_save_csr.""" def setUp(self): zope.component.provideUtility( @@ -61,31 +61,31 @@ class InitSaveCSRTest(unittest.TestCase): def tearDown(self): shutil.rmtree(self.csr_dir) - @mock.patch('letsencrypt.crypto_util.make_csr') - @mock.patch('letsencrypt.crypto_util.le_util.make_or_verify_dir') + @mock.patch('certbot.crypto_util.make_csr') + @mock.patch('certbot.crypto_util.le_util.make_or_verify_dir') def test_it(self, unused_mock_verify, mock_csr): - from letsencrypt.crypto_util import init_save_csr + from certbot.crypto_util import init_save_csr mock_csr.return_value = ('csr_pem', 'csr_der') csr = init_save_csr( mock.Mock(pem='dummy_key'), 'example.com', self.csr_dir, - 'csr-letsencrypt.pem') + 'csr-certbot.pem') self.assertEqual(csr.data, 'csr_der') - self.assertTrue('csr-letsencrypt.pem' in csr.file) + self.assertTrue('csr-certbot.pem' in csr.file) class MakeCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.make_csr.""" + """Tests for certbot.crypto_util.make_csr.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import make_csr + from certbot.crypto_util import make_csr return make_csr(*args, **kwargs) def test_san(self): - from letsencrypt.crypto_util import get_sans_from_csr + from certbot.crypto_util import get_sans_from_csr # TODO: Fails for RSA256_KEY csr_pem, csr_der = self._call( RSA512_KEY, ['example.com', 'www.example.com']) @@ -97,11 +97,11 @@ class MakeCSRTest(unittest.TestCase): class ValidCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.valid_csr.""" + """Tests for certbot.crypto_util.valid_csr.""" @classmethod def _call(cls, csr): - from letsencrypt.crypto_util import valid_csr + from certbot.crypto_util import valid_csr return valid_csr(csr) def test_valid_pem_true(self): @@ -124,11 +124,11 @@ class ValidCSRTest(unittest.TestCase): class CSRMatchesPubkeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.csr_matches_pubkey.""" + """Tests for certbot.crypto_util.csr_matches_pubkey.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import csr_matches_pubkey + from certbot.crypto_util import csr_matches_pubkey return csr_matches_pubkey(*args, **kwargs) def test_valid_true(self): @@ -141,21 +141,21 @@ class CSRMatchesPubkeyTest(unittest.TestCase): class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods - """Tests for letsencrypt.crypto_util.make_key.""" + """Tests for certbot.crypto_util.make_key.""" def test_it(self): # pylint: disable=no-self-use - from letsencrypt.crypto_util import make_key + from certbot.crypto_util import make_key # Do not test larger keys as it takes too long. OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, make_key(1024)) class ValidPrivkeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.valid_privkey.""" + """Tests for certbot.crypto_util.valid_privkey.""" @classmethod def _call(cls, privkey): - from letsencrypt.crypto_util import valid_privkey + from certbot.crypto_util import valid_privkey return valid_privkey(privkey) def test_valid_true(self): @@ -169,11 +169,11 @@ class ValidPrivkeyTest(unittest.TestCase): class GetSANsFromCertTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.get_sans_from_cert.""" + """Tests for certbot.crypto_util.get_sans_from_cert.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import get_sans_from_cert + from certbot.crypto_util import get_sans_from_cert return get_sans_from_cert(*args, **kwargs) def test_single(self): @@ -186,11 +186,11 @@ class GetSANsFromCertTest(unittest.TestCase): class GetSANsFromCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.get_sans_from_csr.""" + """Tests for certbot.crypto_util.get_sans_from_csr.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import get_sans_from_csr + from certbot.crypto_util import get_sans_from_csr return get_sans_from_csr(*args, **kwargs) def test_extract_one_san(self): @@ -216,36 +216,36 @@ class GetSANsFromCSRTest(unittest.TestCase): class CertLoaderTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.pyopenssl_load_certificate""" + """Tests for certbot.crypto_util.pyopenssl_load_certificate""" def test_load_valid_cert(self): - from letsencrypt.crypto_util import pyopenssl_load_certificate + from certbot.crypto_util import pyopenssl_load_certificate cert, file_type = pyopenssl_load_certificate(CERT) self.assertEqual(cert.digest('sha1'), OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha1')) def test_load_invalid_cert(self): - from letsencrypt.crypto_util import pyopenssl_load_certificate + from certbot.crypto_util import pyopenssl_load_certificate bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!") self.assertRaises( errors.Error, pyopenssl_load_certificate, bad_cert_data) class NotBeforeTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.notBefore""" + """Tests for certbot.crypto_util.notBefore""" def test_notBefore(self): - from letsencrypt.crypto_util import notBefore + from certbot.crypto_util import notBefore self.assertEqual(notBefore(CERT_PATH).isoformat(), '2014-12-11T22:34:45+00:00') class NotAfterTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.notAfter""" + """Tests for certbot.crypto_util.notAfter""" def test_notAfter(self): - from letsencrypt.crypto_util import notAfter + from certbot.crypto_util import notAfter self.assertEqual(notAfter(CERT_PATH).isoformat(), '2014-12-18T22:34:45+00:00') diff --git a/certbot/tests/display/__init__.py b/certbot/tests/display/__init__.py new file mode 100644 index 000000000..ec5354e57 --- /dev/null +++ b/certbot/tests/display/__init__.py @@ -0,0 +1 @@ +"""Certbot Display Tests""" diff --git a/letsencrypt/tests/display/completer_test.py b/certbot/tests/display/completer_test.py similarity index 88% rename from letsencrypt/tests/display/completer_test.py rename to certbot/tests/display/completer_test.py index 3c181c925..16805314c 100644 --- a/letsencrypt/tests/display/completer_test.py +++ b/certbot/tests/display/completer_test.py @@ -1,4 +1,4 @@ -"""Test letsencrypt.display.completer.""" +"""Test certbot.display.completer.""" import os import readline import shutil @@ -12,7 +12,7 @@ from six.moves import reload_module # pylint: disable=import-error class CompleterTest(unittest.TestCase): - """Test letsencrypt.display.completer.Completer.""" + """Test certbot.display.completer.Completer.""" def setUp(self): self.temp_dir = tempfile.mkdtemp() @@ -37,7 +37,7 @@ class CompleterTest(unittest.TestCase): shutil.rmtree(self.temp_dir) def test_complete(self): - from letsencrypt.display import completer + from certbot.display import completer my_completer = completer.Completer() num_paths = len(self.paths) @@ -59,7 +59,7 @@ class CompleterTest(unittest.TestCase): sys.modules['readline'] = original_readline def test_context_manager_with_unmocked_readline(self): - from letsencrypt.display import completer + from certbot.display import completer reload_module(completer) original_completer = readline.get_completer() @@ -71,18 +71,18 @@ class CompleterTest(unittest.TestCase): self.assertEqual(readline.get_completer(), original_completer) self.assertEqual(readline.get_completer_delims(), original_delims) - @mock.patch('letsencrypt.display.completer.readline', autospec=True) + @mock.patch('certbot.display.completer.readline', autospec=True) def test_context_manager_libedit(self, mock_readline): mock_readline.__doc__ = 'libedit' self._test_context_manager_with_mock_readline(mock_readline) - @mock.patch('letsencrypt.display.completer.readline', autospec=True) + @mock.patch('certbot.display.completer.readline', autospec=True) def test_context_manager_readline(self, mock_readline): mock_readline.__doc__ = 'GNU readline' self._test_context_manager_with_mock_readline(mock_readline) def _test_context_manager_with_mock_readline(self, mock_readline): - from letsencrypt.display import completer + from certbot.display import completer mock_readline.parse_and_bind.side_effect = enable_tab_completion diff --git a/letsencrypt/tests/display/enhancements_test.py b/certbot/tests/display/enhancements_test.py similarity index 74% rename from letsencrypt/tests/display/enhancements_test.py rename to certbot/tests/display/enhancements_test.py index 6375316bf..b8321d940 100644 --- a/letsencrypt/tests/display/enhancements_test.py +++ b/certbot/tests/display/enhancements_test.py @@ -4,8 +4,8 @@ import unittest import mock -from letsencrypt import errors -from letsencrypt.display import util as display_util +from certbot import errors +from certbot.display import util as display_util class AskTest(unittest.TestCase): @@ -18,10 +18,10 @@ class AskTest(unittest.TestCase): @classmethod def _call(cls, enhancement): - from letsencrypt.display.enhancements import ask + from certbot.display.enhancements import ask return ask(enhancement) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_redirect(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertTrue(self._call("redirect")) @@ -34,20 +34,20 @@ class RedirectTest(unittest.TestCase): """Test the redirect_by_default method.""" @classmethod def _call(cls): - from letsencrypt.display.enhancements import redirect_by_default + from certbot.display.enhancements import redirect_by_default return redirect_by_default() - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_secure(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertTrue(self._call()) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_cancel(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 1) self.assertFalse(self._call()) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_easy(self, mock_util): mock_util().menu.return_value = (display_util.OK, 0) self.assertFalse(self._call()) diff --git a/letsencrypt/tests/display/ops_test.py b/certbot/tests/display/ops_test.py similarity index 85% rename from letsencrypt/tests/display/ops_test.py rename to certbot/tests/display/ops_test.py index 0dacdfea8..05cb6b12d 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/certbot/tests/display/ops_test.py @@ -1,5 +1,5 @@ # coding=utf-8 -"""Test letsencrypt.display.ops.""" +"""Test certbot.display.ops.""" import os import sys import tempfile @@ -11,19 +11,19 @@ import zope.component from acme import jose from acme import messages -from letsencrypt import account -from letsencrypt import interfaces +from certbot import account +from certbot import interfaces -from letsencrypt.display import util as display_util +from certbot.display import util as display_util -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class GetEmailTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.get_email.""" + """Tests for certbot.display.ops.get_email.""" def setUp(self): mock_display = mock.MagicMock() @@ -32,7 +32,7 @@ class GetEmailTest(unittest.TestCase): @classmethod def _call(cls, **kwargs): - from letsencrypt.display.ops import get_email + from certbot.display.ops import get_email return get_email(**kwargs) def test_cancel_none(self): @@ -41,13 +41,13 @@ class GetEmailTest(unittest.TestCase): def test_ok_safe(self): self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.return_value = True self.assertTrue(self._call() is "foo@bar.baz") def test_ok_not_safe(self): self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.side_effect = [False, True] self.assertTrue(self._call() is "foo@bar.baz") @@ -56,7 +56,7 @@ class GetEmailTest(unittest.TestCase): invalid_txt = "There seem to be problems" base_txt = "Enter email" self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.return_value = True self._call() msg = self.input.call_args[0][0] @@ -75,7 +75,7 @@ class GetEmailTest(unittest.TestCase): class ChooseAccountTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.choose_account.""" + """Tests for certbot.display.ops.choose_account.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -86,7 +86,7 @@ class ChooseAccountTest(unittest.TestCase): self.config = mock.MagicMock( accounts_dir=self.accounts_dir, account_keys_dir=self.account_keys_dir, - server="letsencrypt-demo.org") + server="certbot-demo.org") self.key = KEY self.acc1 = account.Account(messages.RegistrationResource( @@ -98,20 +98,20 @@ class ChooseAccountTest(unittest.TestCase): @classmethod def _call(cls, accounts): - from letsencrypt.display import ops + from certbot.display import ops return ops.choose_account(accounts) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_one(self, mock_util): mock_util().menu.return_value = (display_util.OK, 0) self.assertEqual(self._call([self.acc1]), self.acc1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_two(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertEqual(self._call([self.acc1, self.acc2]), self.acc2) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_cancel(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 1) self.assertTrue(self._call([self.acc1, self.acc2]) is None) @@ -124,7 +124,7 @@ class GenSSLLabURLs(unittest.TestCase): @classmethod def _call(cls, domains): - from letsencrypt.display.ops import _gen_ssl_lab_urls + from certbot.display.ops import _gen_ssl_lab_urls return _gen_ssl_lab_urls(domains) def test_zero(self): @@ -143,7 +143,7 @@ class GenHttpsNamesTest(unittest.TestCase): @classmethod def _call(cls, domains): - from letsencrypt.display.ops import _gen_https_names + from certbot.display.ops import _gen_https_names return _gen_https_names(domains) def test_zero(self): @@ -191,20 +191,20 @@ class ChooseNamesTest(unittest.TestCase): @classmethod def _call(cls, installer): - from letsencrypt.display.ops import choose_names + from certbot.display.ops import choose_names return choose_names(installer) - @mock.patch("letsencrypt.display.ops._choose_names_manually") + @mock.patch("certbot.display.ops._choose_names_manually") def test_no_installer(self, mock_manual): self._call(None) self.assertEqual(mock_manual.call_count, 1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_installer_cancel(self, mock_util): mock_util().input.return_value = (display_util.CANCEL, []) self.assertEqual(self._call(None), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_names_choose(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = True @@ -215,14 +215,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(mock_util().input.call_count, 1) self.assertEqual(actual_doms, [domain]) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_names_quit(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = False self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_valid_return(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, ["example.com"]) @@ -231,14 +231,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(names, ["example.com"]) self.assertEqual(mock_util().checklist.call_count, 1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_nothing_selected(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, []) self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_cancel(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = ( @@ -247,7 +247,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(self._call(self.mock_install), []) def test_get_valid_domains(self): - from letsencrypt.display.ops import get_valid_domains + from certbot.display.ops import get_valid_domains all_valid = ["example.com", "second.example.com", "also.example.com"] all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN", @@ -257,9 +257,9 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(get_valid_domains(all_invalid), []) self.assertEqual(len(get_valid_domains(two_valid)), 2) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_choose_manually(self, mock_util): - from letsencrypt.display.ops import _choose_names_manually + from certbot.display.ops import _choose_names_manually # No retry mock_util().yesno.return_value = False # IDN and no retry @@ -268,7 +268,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(_choose_names_manually(), []) # IDN exception with previous mocks with mock.patch( - "letsencrypt.display.ops.display_util.separate_list_input" + "certbot.display.ops.display_util.separate_list_input" ) as mock_sli: unicode_error = UnicodeEncodeError('mock', u'', 0, 1, 'mock') mock_sli.side_effect = unicode_error @@ -302,10 +302,10 @@ class SuccessInstallationTest(unittest.TestCase): """Test the success installation message.""" @classmethod def _call(cls, names): - from letsencrypt.display.ops import success_installation + from certbot.display.ops import success_installation success_installation(names) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_success_installation(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] @@ -324,10 +324,10 @@ class SuccessRenewalTest(unittest.TestCase): """Test the success renewal message.""" @classmethod def _call(cls, names): - from letsencrypt.display.ops import success_renewal + from certbot.display.ops import success_renewal success_renewal(names, "renew") - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_success_renewal(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] diff --git a/letsencrypt/tests/display/util_test.py b/certbot/tests/display/util_test.py similarity index 91% rename from letsencrypt/tests/display/util_test.py rename to certbot/tests/display/util_test.py index bae0d582a..4a38803d1 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/certbot/tests/display/util_test.py @@ -1,12 +1,12 @@ -"""Test :mod:`letsencrypt.display.util`.""" +"""Test :mod:`certbot.display.util`.""" import os import unittest import mock -import letsencrypt.errors as errors +import certbot.errors as errors -from letsencrypt.display import util as display_util +from certbot.display import util as display_util CHOICES = [("First", "Description1"), ("Second", "Description2")] @@ -40,13 +40,13 @@ class NcursesDisplayTest(unittest.TestCase): "menu_height": display_util.HEIGHT - 6, } - @mock.patch("letsencrypt.display.util.dialog.Dialog.msgbox") + @mock.patch("certbot.display.util.dialog.Dialog.msgbox") def test_notification(self, mock_msgbox): """Kind of worthless... one liner.""" self.displayer.notification("message") self.assertEqual(mock_msgbox.call_count, 1) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_tag_and_desc(self, mock_menu): mock_menu.return_value = (display_util.OK, "First") @@ -55,7 +55,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.OK, 0)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_tag_and_desc_cancel(self, mock_menu): mock_menu.return_value = (display_util.CANCEL, "") @@ -65,7 +65,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.CANCEL, -1)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only(self, mock_menu): mock_menu.return_value = (display_util.OK, "1") @@ -77,7 +77,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.OK, 0)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only_help(self, mock_menu): mock_menu.return_value = (display_util.HELP, "2") @@ -85,7 +85,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.HELP, 1)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only_cancel(self, mock_menu): mock_menu.return_value = (display_util.CANCEL, "") @@ -93,13 +93,13 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.CANCEL, -1)) - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "dialog.Dialog.inputbox") def test_input(self, mock_input): self.displayer.input("message") self.assertEqual(mock_input.call_count, 1) - @mock.patch("letsencrypt.display.util.dialog.Dialog.yesno") + @mock.patch("certbot.display.util.dialog.Dialog.yesno") def test_yesno(self, mock_yesno): mock_yesno.return_value = display_util.OK @@ -109,7 +109,7 @@ class NcursesDisplayTest(unittest.TestCase): "message", display_util.HEIGHT, display_util.WIDTH, yes_label="Yes", no_label="No") - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "dialog.Dialog.checklist") def test_checklist(self, mock_checklist): self.displayer.checklist("message", TAGS) @@ -123,7 +123,7 @@ class NcursesDisplayTest(unittest.TestCase): "message", width=display_util.WIDTH, height=display_util.HEIGHT, choices=choices) - @mock.patch("letsencrypt.display.util.dialog.Dialog.dselect") + @mock.patch("certbot.display.util.dialog.Dialog.dselect") def test_directory_select(self, mock_dselect): self.displayer.directory_select("message") self.assertEqual(mock_dselect.call_count, 1) @@ -153,7 +153,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.assertTrue("message" in self.mock_stdout.write.call_args[0][0]) - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "FileDisplay._get_valid_int_ans") def test_menu(self, mock_ans): mock_ans.return_value = (display_util.OK, 1) @@ -188,14 +188,14 @@ class FileOutputDisplayTest(unittest.TestCase): with mock.patch("__builtin__.raw_input", return_value="a"): self.assertTrue(self.displayer.yesno("msg", yes_label="Agree")) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_valid(self, mock_input): mock_input.return_value = (display_util.OK, "2 1") code, tag_list = self.displayer.checklist("msg", TAGS) self.assertEqual( (code, set(tag_list)), (display_util.OK, set(["tag1", "tag2"]))) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_miss_valid(self, mock_input): mock_input.side_effect = [ (display_util.OK, "10"), @@ -206,7 +206,7 @@ class FileOutputDisplayTest(unittest.TestCase): ret = self.displayer.checklist("msg", TAGS) self.assertEqual(ret, (display_util.OK, ["tag1"])) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_miss_quit(self, mock_input): mock_input.side_effect = [ (display_util.OK, "10"), @@ -232,7 +232,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.displayer._scrub_checklist_input(list_, TAGS)) self.assertEqual(set_tags, exp[i]) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_directory_select(self, mock_input): message = "msg" result = (display_util.OK, "/var/www/html",) @@ -352,7 +352,7 @@ class SeparateListInputTest(unittest.TestCase): @classmethod def _call(cls, input_): - from letsencrypt.display.util import separate_list_input + from certbot.display.util import separate_list_input return separate_list_input(input_) def test_commas(self): @@ -378,7 +378,7 @@ class SeparateListInputTest(unittest.TestCase): class PlaceParensTest(unittest.TestCase): @classmethod def _call(cls, label): # pylint: disable=protected-access - from letsencrypt.display.util import _parens_around_char + from certbot.display.util import _parens_around_char return _parens_around_char(label) def test_single_letter(self): diff --git a/letsencrypt/tests/error_handler_test.py b/certbot/tests/error_handler_test.py similarity index 90% rename from letsencrypt/tests/error_handler_test.py rename to certbot/tests/error_handler_test.py index 7fbdcffd8..5434b36be 100644 --- a/letsencrypt/tests/error_handler_test.py +++ b/certbot/tests/error_handler_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.error_handler.""" +"""Tests for certbot.error_handler.""" import signal import sys import unittest @@ -7,10 +7,10 @@ import mock class ErrorHandlerTest(unittest.TestCase): - """Tests for letsencrypt.error_handler.""" + """Tests for certbot.error_handler.""" def setUp(self): - from letsencrypt import error_handler + from certbot import error_handler self.init_func = mock.MagicMock() self.init_args = set((42,)) @@ -30,8 +30,8 @@ class ErrorHandlerTest(unittest.TestCase): self.init_func.assert_called_once_with(*self.init_args, **self.init_kwargs) - @mock.patch('letsencrypt.error_handler.os') - @mock.patch('letsencrypt.error_handler.signal') + @mock.patch('certbot.error_handler.os') + @mock.patch('certbot.error_handler.signal') def test_signal_handler(self, mock_signal, mock_os): # pylint: disable=protected-access mock_signal.getsignal.return_value = signal.SIG_DFL diff --git a/letsencrypt/tests/errors_test.py b/certbot/tests/errors_test.py similarity index 74% rename from letsencrypt/tests/errors_test.py rename to certbot/tests/errors_test.py index 5da7c0b7a..67611ed45 100644 --- a/letsencrypt/tests/errors_test.py +++ b/certbot/tests/errors_test.py @@ -1,19 +1,19 @@ -"""Tests for letsencrypt.errors.""" +"""Tests for certbot.errors.""" import unittest import mock from acme import messages -from letsencrypt import achallenges -from letsencrypt.tests import acme_util +from certbot import achallenges +from certbot.tests import acme_util class FaiiledChallengesTest(unittest.TestCase): - """Tests for letsencrypt.errors.FailedChallenges.""" + """Tests for certbot.errors.FailedChallenges.""" def setUp(self): - from letsencrypt.errors import FailedChallenges + from certbot.errors import FailedChallenges self.error = FailedChallenges(set([achallenges.DNS( domain="example.com", challb=messages.ChallengeBody( chall=acme_util.DNS, uri=None, @@ -25,10 +25,10 @@ class FaiiledChallengesTest(unittest.TestCase): class StandaloneBindErrorTest(unittest.TestCase): - """Tests for letsencrypt.errors.StandaloneBindError.""" + """Tests for certbot.errors.StandaloneBindError.""" def setUp(self): - from letsencrypt.errors import StandaloneBindError + from certbot.errors import StandaloneBindError self.error = StandaloneBindError(mock.sentinel.error, 1234) def test_instance_args(self): diff --git a/certbot/tests/hook_test.py b/certbot/tests/hook_test.py new file mode 100644 index 000000000..ce78b5dc9 --- /dev/null +++ b/certbot/tests/hook_test.py @@ -0,0 +1,101 @@ +"""Tests for hooks.py""" +# pylint: disable=protected-access + +import os +import unittest + +import mock + +from certbot import errors +from certbot import hooks + +class HookTest(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + @mock.patch('certbot.hooks._prog') + def test_validate_hooks(self, mock_prog): + config = mock.MagicMock(pre_hook="", post_hook="ls -lR", renew_hook="uptime") + hooks.validate_hooks(config) + self.assertEqual(mock_prog.call_count, 2) + self.assertEqual(mock_prog.call_args_list[1][0][0], 'uptime') + self.assertEqual(mock_prog.call_args_list[0][0][0], 'ls') + mock_prog.return_value = None + config = mock.MagicMock(pre_hook="explodinator", post_hook="", renew_hook="") + self.assertRaises(errors.HookCommandNotFound, hooks.validate_hooks, config) + + @mock.patch('certbot.hooks._is_exe') + def test_which(self, mock_is_exe): + mock_is_exe.return_value = True + self.assertEqual(hooks._which("/path/to/something"), "/path/to/something") + + with mock.patch.dict('os.environ', {"PATH": "/floop:/fleep"}): + mock_is_exe.return_value = True + self.assertEqual(hooks._which("pingify"), "/floop/pingify") + mock_is_exe.return_value = False + self.assertEqual(hooks._which("pingify"), None) + self.assertEqual(hooks._which("/path/to/something"), None) + + @mock.patch('certbot.hooks._which') + def test_prog(self, mockwhich): + mockwhich.return_value = "/very/very/funky" + self.assertEqual(hooks._prog("funky"), "funky") + mockwhich.return_value = None + self.assertEqual(hooks._prog("funky"), None) + + def _test_a_hook(self, config, hook_function, calls_expected): + with mock.patch('certbot.hooks.logger') as mock_logger: + mock_logger.warning = mock.MagicMock() + with mock.patch('certbot.hooks._run_hook') as mock_run_hook: + hook_function(config) + hook_function(config) + self.assertEqual(mock_run_hook.call_count, calls_expected) + return mock_logger.warning + + def test_pre_hook(self): + config = mock.MagicMock(pre_hook="true") + self._test_a_hook(config, hooks.pre_hook, 1) + config = mock.MagicMock(pre_hook="") + self._test_a_hook(config, hooks.pre_hook, 0) + + def test_post_hook(self): + config = mock.MagicMock(post_hook="true", verb="splonk") + self._test_a_hook(config, hooks.post_hook, 2) + config = mock.MagicMock(post_hook="true", verb="renew") + self._test_a_hook(config, hooks.post_hook, 0) + + def test_renew_hook(self): + with mock.patch.dict('os.environ', {}): + domains = ["a", "b"] + lineage = "thing" + rhook = lambda x: hooks.renew_hook(x, domains, lineage) + + config = mock.MagicMock(renew_hook="true", dry_run=False) + self._test_a_hook(config, rhook, 2) + self.assertEqual(os.environ["RENEWED_DOMAINS"], "a b") + self.assertEqual(os.environ["RENEWED_LINEAGE"], "thing") + + config = mock.MagicMock(renew_hook="true", dry_run=True) + mock_warn = self._test_a_hook(config, rhook, 0) + self.assertEqual(mock_warn.call_count, 2) + + @mock.patch('certbot.hooks.Popen') + def test_run_hook(self, mock_popen): + with mock.patch('certbot.hooks.logger.error') as mock_error: + mock_cmd = mock.MagicMock() + mock_cmd.returncode = 1 + mock_cmd.communicate.return_value = ("", "") + mock_popen.return_value = mock_cmd + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 1) + with mock.patch('certbot.hooks.logger.error') as mock_error: + mock_cmd.communicate.return_value = ("", "thing") + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 2) + + +if __name__ == '__main__': + unittest.main() # pragma: no cover diff --git a/letsencrypt/tests/le_util_test.py b/certbot/tests/le_util_test.py similarity index 83% rename from letsencrypt/tests/le_util_test.py rename to certbot/tests/le_util_test.py index 0f9464c6f..b6da4525f 100644 --- a/letsencrypt/tests/le_util_test.py +++ b/certbot/tests/le_util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.le_util.""" +"""Tests for certbot.le_util.""" import argparse import errno import os @@ -10,17 +10,17 @@ import unittest import mock import six -from letsencrypt import errors +from certbot import errors class RunScriptTest(unittest.TestCase): - """Tests for letsencrypt.le_util.run_script.""" + """Tests for certbot.le_util.run_script.""" @classmethod def _call(cls, params): - from letsencrypt.le_util import run_script + from certbot.le_util import run_script return run_script(params) - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_default(self, mock_popen): """These will be changed soon enough with reload.""" mock_popen().returncode = 0 @@ -30,13 +30,13 @@ class RunScriptTest(unittest.TestCase): self.assertEqual(out, "stdout") self.assertEqual(err, "stderr") - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_bad_process(self, mock_popen): mock_popen.side_effect = OSError self.assertRaises(errors.SubprocessError, self._call, ["test"]) - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_failure(self, mock_popen): mock_popen().communicate.return_value = ("", "") mock_popen().returncode = 1 @@ -45,29 +45,29 @@ class RunScriptTest(unittest.TestCase): class ExeExistsTest(unittest.TestCase): - """Tests for letsencrypt.le_util.exe_exists.""" + """Tests for certbot.le_util.exe_exists.""" @classmethod def _call(cls, exe): - from letsencrypt.le_util import exe_exists + from certbot.le_util import exe_exists return exe_exists(exe) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_full_path(self, mock_access, mock_isfile): mock_access.return_value = True mock_isfile.return_value = True self.assertTrue(self._call("/path/to/exe")) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_on_path(self, mock_access, mock_isfile): mock_access.return_value = True mock_isfile.return_value = True self.assertTrue(self._call("exe")) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_not_found(self, mock_access, mock_isfile): mock_access.return_value = False mock_isfile.return_value = True @@ -75,7 +75,7 @@ class ExeExistsTest(unittest.TestCase): class MakeOrVerifyDirTest(unittest.TestCase): - """Tests for letsencrypt.le_util.make_or_verify_dir. + """Tests for certbot.le_util.make_or_verify_dir. Note that it is not possible to test for a wrong directory owner, as this testing script would have to be run as root. @@ -93,7 +93,7 @@ class MakeOrVerifyDirTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, directory, mode): - from letsencrypt.le_util import make_or_verify_dir + from certbot.le_util import make_or_verify_dir return make_or_verify_dir(directory, mode, self.uid, strict=True) def test_creates_dir_when_missing(self): @@ -116,7 +116,7 @@ class MakeOrVerifyDirTest(unittest.TestCase): class CheckPermissionsTest(unittest.TestCase): - """Tests for letsencrypt.le_util.check_permissions. + """Tests for certbot.le_util.check_permissions. Note that it is not possible to test for a wrong file owner, as this testing script would have to be run as root. @@ -131,7 +131,7 @@ class CheckPermissionsTest(unittest.TestCase): os.remove(self.path) def _call(self, mode): - from letsencrypt.le_util import check_permissions + from certbot.le_util import check_permissions return check_permissions(self.path, mode, self.uid) def test_ok_mode(self): @@ -144,7 +144,7 @@ class CheckPermissionsTest(unittest.TestCase): class UniqueFileTest(unittest.TestCase): - """Tests for letsencrypt.le_util.unique_file.""" + """Tests for certbot.le_util.unique_file.""" def setUp(self): self.root_path = tempfile.mkdtemp() @@ -154,7 +154,7 @@ class UniqueFileTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, mode=0o600): - from letsencrypt.le_util import unique_file + from certbot.le_util import unique_file return unique_file(self.default_name, mode) def test_returns_fd_for_writing(self): @@ -189,7 +189,7 @@ class UniqueFileTest(unittest.TestCase): class UniqueLineageNameTest(unittest.TestCase): - """Tests for letsencrypt.le_util.unique_lineage_name.""" + """Tests for certbot.le_util.unique_lineage_name.""" def setUp(self): self.root_path = tempfile.mkdtemp() @@ -198,7 +198,7 @@ class UniqueLineageNameTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, filename, mode=0o777): - from letsencrypt.le_util import unique_lineage_name + from certbot.le_util import unique_lineage_name return unique_lineage_name(self.root_path, filename, mode) def test_basic(self): @@ -213,14 +213,14 @@ class UniqueLineageNameTest(unittest.TestCase): self.assertTrue(isinstance(name, str)) self.assertTrue("wow-0009.conf" in name) - @mock.patch("letsencrypt.le_util.os.fdopen") + @mock.patch("certbot.le_util.os.fdopen") def test_failure(self, mock_fdopen): err = OSError("whoops") err.errno = errno.EIO mock_fdopen.side_effect = err self.assertRaises(OSError, self._call, "wow") - @mock.patch("letsencrypt.le_util.os.fdopen") + @mock.patch("certbot.le_util.os.fdopen") def test_subsequent_failure(self, mock_fdopen): self._call("wow") err = OSError("whoops") @@ -230,7 +230,7 @@ class UniqueLineageNameTest(unittest.TestCase): class SafelyRemoveTest(unittest.TestCase): - """Tests for letsencrypt.le_util.safely_remove.""" + """Tests for certbot.le_util.safely_remove.""" def setUp(self): self.tmp = tempfile.mkdtemp() @@ -240,7 +240,7 @@ class SafelyRemoveTest(unittest.TestCase): shutil.rmtree(self.tmp) def _call(self): - from letsencrypt.le_util import safely_remove + from certbot.le_util import safely_remove return safely_remove(self.path) def test_exists(self): @@ -254,7 +254,7 @@ class SafelyRemoveTest(unittest.TestCase): # no error, yay! self.assertFalse(os.path.exists(self.path)) - @mock.patch("letsencrypt.le_util.os.remove") + @mock.patch("certbot.le_util.os.remove") def test_other_error_passthrough(self, mock_remove): mock_remove.side_effect = OSError self.assertRaises(OSError, self._call) @@ -264,12 +264,12 @@ class SafeEmailTest(unittest.TestCase): """Test safe_email.""" @classmethod def _call(cls, addr): - from letsencrypt.le_util import safe_email + from certbot.le_util import safe_email return safe_email(addr) def test_valid_emails(self): addrs = [ - "letsencrypt@letsencrypt.org", + "certbot@certbot.org", "tbd.ade@gmail.com", "abc_def.jdk@hotmail.museum", ] @@ -278,7 +278,7 @@ class SafeEmailTest(unittest.TestCase): def test_invalid_emails(self): addrs = [ - "letsencrypt@letsencrypt..org", + "certbot@certbot..org", ".tbd.ade@gmail.com", "~/abc_def.jdk@hotmail.museum", ] @@ -292,7 +292,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase): self.parser = argparse.ArgumentParser() def _call(self, argument_name, nargs): - from letsencrypt.le_util import add_deprecated_argument + from certbot.le_util import add_deprecated_argument add_deprecated_argument(self.parser.add_argument, argument_name, nargs) @@ -308,14 +308,14 @@ class AddDeprecatedArgumentTest(unittest.TestCase): def _get_argparse_warnings(self, args): stderr = six.StringIO() - with mock.patch("letsencrypt.le_util.sys.stderr", new=stderr): + with mock.patch("certbot.le_util.sys.stderr", new=stderr): self.parser.parse_args(args) return stderr.getvalue() def test_help(self): self._call("--old-option", 2) stdout = six.StringIO() - with mock.patch("letsencrypt.le_util.sys.stdout", new=stdout): + with mock.patch("certbot.le_util.sys.stdout", new=stdout): try: self.parser.parse_args(["-h"]) except SystemExit: @@ -327,7 +327,7 @@ class EnforceDomainSanityTest(unittest.TestCase): """Test enforce_domain_sanity.""" def _call(self, domain): - from letsencrypt.le_util import enforce_domain_sanity + from certbot.le_util import enforce_domain_sanity return enforce_domain_sanity(domain) def test_nonascii_str(self): diff --git a/letsencrypt/tests/log_test.py b/certbot/tests/log_test.py similarity index 95% rename from letsencrypt/tests/log_test.py rename to certbot/tests/log_test.py index c1afd2c8a..a4f394870 100644 --- a/letsencrypt/tests/log_test.py +++ b/certbot/tests/log_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.log.""" +"""Tests for certbot.log.""" import logging import unittest @@ -10,7 +10,7 @@ class DialogHandlerTest(unittest.TestCase): def setUp(self): self.d = mock.MagicMock() - from letsencrypt.log import DialogHandler + from certbot.log import DialogHandler self.handler = DialogHandler(height=2, width=6, d=self.d) self.handler.PADDING_HEIGHT = 2 self.handler.PADDING_WIDTH = 4 diff --git a/letsencrypt/tests/notify_test.py b/certbot/tests/notify_test.py similarity index 79% rename from letsencrypt/tests/notify_test.py rename to certbot/tests/notify_test.py index 60364fff8..d2af5b001 100644 --- a/letsencrypt/tests/notify_test.py +++ b/certbot/tests/notify_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.notify.""" +"""Tests for certbot.notify.""" import socket import unittest @@ -8,9 +8,9 @@ import mock class NotifyTests(unittest.TestCase): """Tests for the notifier.""" - @mock.patch("letsencrypt.notify.smtplib.LMTP") + @mock.patch("certbot.notify.smtplib.LMTP") def test_smtp_success(self, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj self.assertTrue(notify("Goose", "auntrhody@example.com", @@ -18,10 +18,10 @@ class NotifyTests(unittest.TestCase): self.assertEqual(lmtp_obj.connect.call_count, 1) self.assertEqual(lmtp_obj.sendmail.call_count, 1) - @mock.patch("letsencrypt.notify.smtplib.LMTP") - @mock.patch("letsencrypt.notify.subprocess.Popen") + @mock.patch("certbot.notify.smtplib.LMTP") + @mock.patch("certbot.notify.subprocess.Popen") def test_smtp_failure(self, mock_popen, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj lmtp_obj.sendmail.side_effect = socket.error(17) @@ -32,10 +32,10 @@ class NotifyTests(unittest.TestCase): self.assertEqual(lmtp_obj.sendmail.call_count, 1) self.assertEqual(proc.communicate.call_count, 1) - @mock.patch("letsencrypt.notify.smtplib.LMTP") - @mock.patch("letsencrypt.notify.subprocess.Popen") + @mock.patch("certbot.notify.smtplib.LMTP") + @mock.patch("certbot.notify.subprocess.Popen") def test_everything_fails(self, mock_popen, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj lmtp_obj.sendmail.side_effect = socket.error(17) diff --git a/letsencrypt/tests/reporter_test.py b/certbot/tests/reporter_test.py similarity index 93% rename from letsencrypt/tests/reporter_test.py rename to certbot/tests/reporter_test.py index 26a1105c8..02c7981b7 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/certbot/tests/reporter_test.py @@ -1,4 +1,5 @@ -"""Tests for letsencrypt.reporter.""" +"""Tests for certbot.reporter.""" +import mock import sys import unittest @@ -6,11 +7,11 @@ import six class ReporterTest(unittest.TestCase): - """Tests for letsencrypt.reporter.Reporter.""" + """Tests for certbot.reporter.Reporter.""" def setUp(self): - from letsencrypt import reporter - self.reporter = reporter.Reporter() + from certbot import reporter + self.reporter = reporter.Reporter(mock.MagicMock(quiet=False)) self.old_stdout = sys.stdout sys.stdout = six.StringIO() diff --git a/letsencrypt/tests/reverter_test.py b/certbot/tests/reverter_test.py similarity index 94% rename from letsencrypt/tests/reverter_test.py rename to certbot/tests/reverter_test.py index aafd3b041..eda5ffb36 100644 --- a/letsencrypt/tests/reverter_test.py +++ b/certbot/tests/reverter_test.py @@ -1,4 +1,4 @@ -"""Test letsencrypt.reverter.""" +"""Test certbot.reverter.""" import csv import itertools import logging @@ -9,14 +9,14 @@ import unittest import mock -from letsencrypt import errors +from certbot import errors class ReverterCheckpointLocalTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes, too-many-public-methods """Test the Reverter Class.""" def setUp(self): - from letsencrypt.reverter import Reverter + from certbot.reverter import Reverter # Disable spurious errors... we are trying to test for them logging.disable(logging.CRITICAL) @@ -50,7 +50,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): "{0}\n{1}\n".format(self.config1, self.config2)) def test_add_to_checkpoint_copy_failure(self): - with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2: + with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2: mock_copy2.side_effect = IOError("bad copy") self.assertRaises( errors.ReverterError, self.reverter.add_to_checkpoint, @@ -96,7 +96,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): self.reverter.register_file_creation(True, self.config2) self.reverter.register_file_creation(True, config3, config4) - # Simulate Let's Encrypt crash... recovery routine is run + # Simulate Certbot crash... recovery routine is run self.reverter.recovery_routine() self.assertFalse(os.path.isfile(self.config1)) @@ -116,7 +116,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_register_file_creation_write_error(self): m_open = mock.mock_open() - with mock.patch("letsencrypt.reverter.open", m_open, create=True): + with mock.patch("certbot.reverter.open", m_open, create=True): m_open.side_effect = OSError("bad open") self.assertRaises( errors.ReverterError, self.reverter.register_file_creation, @@ -144,13 +144,13 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_bad_register_undo_command(self): m_open = mock.mock_open() - with mock.patch("letsencrypt.reverter.open", m_open, create=True): + with mock.patch("certbot.reverter.open", m_open, create=True): m_open.side_effect = OSError("bad open") self.assertRaises( errors.ReverterError, self.reverter.register_undo_command, True, ["command"]) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_run_undo_commands(self, mock_run): mock_run.side_effect = ["", errors.SubprocessError] coms = [ @@ -200,7 +200,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_recover_checkpoint_copy_failure(self): self.reverter.add_to_temp_checkpoint(self.sets[0], "save1") - with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2: + with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2: mock_copy2.side_effect = OSError("bad copy") self.assertRaises( errors.ReverterError, self.reverter.revert_temporary_config) @@ -208,19 +208,19 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_recover_checkpoint_rm_failure(self): self.reverter.add_to_temp_checkpoint(self.sets[0], "temp save") - with mock.patch("letsencrypt.reverter.shutil.rmtree") as mock_rmtree: + with mock.patch("certbot.reverter.shutil.rmtree") as mock_rmtree: mock_rmtree.side_effect = OSError("Cannot remove tree") self.assertRaises( errors.ReverterError, self.reverter.revert_temporary_config) - @mock.patch("letsencrypt.reverter.logger.warning") + @mock.patch("certbot.reverter.logger.warning") def test_recover_checkpoint_missing_new_files(self, mock_warn): self.reverter.register_file_creation( True, os.path.join(self.dir1, "missing_file.txt")) self.reverter.revert_temporary_config() self.assertEqual(mock_warn.call_count, 1) - @mock.patch("letsencrypt.reverter.os.remove") + @mock.patch("certbot.reverter.os.remove") def test_recover_checkpoint_remove_failure(self, mock_remove): self.reverter.register_file_creation(True, self.config1) mock_remove.side_effect = OSError("Can't remove") @@ -265,7 +265,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # pylint: disable=too-many-instance-attributes """Tests functions having to deal with full checkpoints.""" def setUp(self): - from letsencrypt.reverter import Reverter + from certbot.reverter import Reverter # Disable spurious errors... logging.disable(logging.CRITICAL) @@ -324,7 +324,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # No need to warn for this... just make sure there are no errors. self.reverter.finalize_checkpoint("No checkpoint...") - @mock.patch("letsencrypt.reverter.shutil.move") + @mock.patch("certbot.reverter.shutil.move") def test_finalize_checkpoint_cannot_title(self, mock_move): self.reverter.add_to_checkpoint(self.sets[0], "perm save") mock_move.side_effect = OSError("cannot move") @@ -332,7 +332,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertRaises( errors.ReverterError, self.reverter.finalize_checkpoint, "Title") - @mock.patch("letsencrypt.reverter.os.rename") + @mock.patch("certbot.reverter.os.rename") def test_finalize_checkpoint_no_rename_directory(self, mock_rename): self.reverter.add_to_checkpoint(self.sets[0], "perm save") @@ -341,7 +341,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertRaises( errors.ReverterError, self.reverter.finalize_checkpoint, "Title") - @mock.patch("letsencrypt.reverter.logger") + @mock.patch("certbot.reverter.logger") def test_rollback_too_many(self, mock_logger): # Test no exist warning... self.reverter.rollback_checkpoints(1) @@ -361,7 +361,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertEqual(read_in(self.config2), "directive-dir2") self.assertFalse(os.path.isfile(config3)) - @mock.patch("letsencrypt.reverter.zope.component.getUtility") + @mock.patch("certbot.reverter.zope.component.getUtility") def test_view_config_changes(self, mock_output): """This is not strict as this is subject to change.""" self._setup_three_checkpoints() @@ -372,7 +372,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # Make sure notification is output self.assertEqual(mock_output().notification.call_count, 1) - @mock.patch("letsencrypt.reverter.logger") + @mock.patch("certbot.reverter.logger") def test_view_config_changes_no_backups(self, mock_logger): self.reverter.view_config_changes() self.assertTrue(mock_logger.info.call_count > 0) @@ -426,7 +426,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): def setup_work_direc(): """Setup directories. - :returns: Mocked :class:`letsencrypt.interfaces.IConfig` + :returns: Mocked :class:`certbot.interfaces.IConfig` """ work_dir = tempfile.mkdtemp("work") diff --git a/letsencrypt/tests/storage_test.py b/certbot/tests/storage_test.py similarity index 93% rename from letsencrypt/tests/storage_test.py rename to certbot/tests/storage_test.py index 49b4f0821..be626edc5 100644 --- a/letsencrypt/tests/storage_test.py +++ b/certbot/tests/storage_test.py @@ -1,4 +1,5 @@ -"""Tests for letsencrypt.storage.""" +"""Tests for certbot.storage.""" +# pylint disable=protected-access import datetime import os import shutil @@ -9,11 +10,11 @@ import configobj import mock import pytz -from letsencrypt import configuration -from letsencrypt import errors -from letsencrypt.storage import ALL_FOUR +from certbot import configuration +from certbot import errors +from certbot.storage import ALL_FOUR -from letsencrypt.tests import test_util +from certbot.tests import test_util CERT = test_util.load_cert('cert.pem') @@ -40,7 +41,7 @@ class BaseRenewableCertTest(unittest.TestCase): """ def setUp(self): - from letsencrypt import storage + from certbot import storage self.tempdir = tempfile.mkdtemp() self.cli_config = configuration.RenewerConfiguration( @@ -75,7 +76,7 @@ class BaseRenewableCertTest(unittest.TestCase): self.defaults = configobj.ConfigObj() - with mock.patch("letsencrypt.storage.RenewableCert._check_symlinks") as check: + with mock.patch("certbot.storage.RenewableCert._check_symlinks") as check: check.return_value = True self.test_rc = storage.RenewableCert(config.filename, self.cli_config) @@ -98,7 +99,7 @@ class BaseRenewableCertTest(unittest.TestCase): class RenewableCertTests(BaseRenewableCertTest): # pylint: disable=too-many-public-methods - """Tests for letsencrypt.storage.""" + """Tests for certbot.storage.""" def test_initialization(self): self.assertEqual(self.test_rc.lineagename, "example.org") @@ -112,7 +113,7 @@ class RenewableCertTests(BaseRenewableCertTest): the renewal configuration file doesn't end in ".conf" """ - from letsencrypt import storage + from certbot import storage broken = os.path.join(self.tempdir, "broken.conf") with open(broken, "w") as f: f.write("[No closing bracket for you!") @@ -125,7 +126,7 @@ class RenewableCertTests(BaseRenewableCertTest): def test_renewal_incomplete_config(self): """Test that the RenewableCert constructor will complain if the renewal configuration file is missing a required file element.""" - from letsencrypt import storage + from certbot import storage config = configobj.ConfigObj() config["cert"] = "imaginary_cert.pem" # Here the required privkey is missing. @@ -326,7 +327,7 @@ class RenewableCertTests(BaseRenewableCertTest): real_unlink(path) self._write_out_ex_kinds() - with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + with mock.patch("certbot.storage.os.unlink") as mock_unlink: mock_unlink.side_effect = unlink_or_raise self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) @@ -342,7 +343,7 @@ class RenewableCertTests(BaseRenewableCertTest): real_unlink(path) self._write_out_ex_kinds() - with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + with mock.patch("certbot.storage.os.unlink") as mock_unlink: mock_unlink.side_effect = unlink_or_raise self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) @@ -393,7 +394,7 @@ class RenewableCertTests(BaseRenewableCertTest): os.unlink(self.test_rc.cert) self.assertRaises(errors.CertStorageError, self.test_rc.names) - @mock.patch("letsencrypt.storage.datetime") + @mock.patch("certbot.storage.datetime") def test_time_interval_judgments(self, mock_datetime): """Test should_autodeploy() and should_autorenew() on the basis of expiry time windows.""" @@ -473,7 +474,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.test_rc.configuration["autorenew"] = "0" self.assertFalse(self.test_rc.autorenewal_is_enabled()) - @mock.patch("letsencrypt.storage.RenewableCert.ocsp_revoked") + @mock.patch("certbot.storage.RenewableCert.ocsp_revoked") def test_should_autorenew(self, mock_ocsp): """Test should_autorenew on the basis of reasons other than expiry time window.""" @@ -493,7 +494,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertTrue(self.test_rc.should_autorenew()) mock_ocsp.return_value = False - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_save_successor(self, mock_rv): # Mock relevant_values() to claim that all values are relevant here # (to avoid instantiating parser) @@ -562,33 +563,33 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10))) self.assertFalse(os.path.exists(temp_config_file)) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values(self, mock_parser): """Test that relevant_values() can reject an irrelevant value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"hello": "there"}), {}) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values_default(self, mock_parser): """Test that relevant_values() can reject a default value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {}) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values_nondefault(self, mock_parser): """Test that relevant_values() can retain a non-default value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) @@ -596,14 +597,14 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertEqual(storage.relevant_values({"rsa_key_size": 12}), {"rsa_key_size": 12}) - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_new_lineage(self, mock_rv): """Test for new_lineage() class method.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x - from letsencrypt import storage + from certbot import storage result = storage.RenewableCert.new_lineage( "the-lineage.com", "cert", "privkey", "chain", self.cli_config) # This consistency check tests most relevant properties about the @@ -636,14 +637,14 @@ class RenewableCertTests(BaseRenewableCertTest): # TODO: Conceivably we could test that the renewal parameters actually # got saved - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_new_lineage_nonexistent_dirs(self, mock_rv): """Test that directories can be created if they don't exist.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x - from letsencrypt import storage + from certbot import storage shutil.rmtree(self.cli_config.renewal_configs_dir) shutil.rmtree(self.cli_config.archive_dir) shutil.rmtree(self.cli_config.live_dir) @@ -658,9 +659,9 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertTrue(os.path.exists(os.path.join( self.cli_config.archive_dir, "the-lineage.com", "privkey1.pem"))) - @mock.patch("letsencrypt.storage.le_util.unique_lineage_name") + @mock.patch("certbot.storage.le_util.unique_lineage_name") def test_invalid_config_filename(self, mock_uln): - from letsencrypt import storage + from certbot import storage mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes" self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "example.com", @@ -690,7 +691,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(self.test_rc.ocsp_revoked()) def test_add_time_interval(self): - from letsencrypt import storage + from certbot import storage # this month has 30 days, and the next year is a leap year time_1 = pytz.UTC.fromutc(datetime.datetime(2003, 11, 20, 11, 59, 21)) @@ -732,7 +733,7 @@ class RenewableCertTests(BaseRenewableCertTest): excepted) def test_missing_cert(self): - from letsencrypt import storage + from certbot import storage self.assertRaises(errors.CertStorageError, storage.RenewableCert, self.config.filename, self.cli_config) @@ -741,6 +742,29 @@ class RenewableCertTests(BaseRenewableCertTest): storage.RenewableCert, self.config.filename, self.cli_config) + def test_write_renewal_config(self): + # Mostly tested by the process of creating and updating lineages, + # but we can test that this successfully creates files, removes + # unneeded items, and preserves comments. + temp = os.path.join(self.tempdir, "sample-file") + temp2 = os.path.join(self.tempdir, "sample-file.new") + with open(temp, "w") as f: + f.write("[renewalparams]\nuseful = value # A useful value\n" + "useless = value # Not needed\n") + target = {} + for x in ALL_FOUR: + target[x] = "somewhere" + relevant_data = {"useful": "new_value"} + from certbot import storage + storage.write_renewal_config(temp, temp2, target, relevant_data) + with open(temp2, "r") as f: + content = f.read() + # useful value was updated + assert "useful = new_value" in content + # associated comment was preserved + assert "A useful value" in content + # useless value was deleted + assert "useless" not in content if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/letsencrypt/tests/test_util.py b/certbot/tests/test_util.py similarity index 100% rename from letsencrypt/tests/test_util.py rename to certbot/tests/test_util.py diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem b/certbot/tests/testdata/archive/sample-renewal/cert1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem rename to certbot/tests/testdata/archive/sample-renewal/cert1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem b/certbot/tests/testdata/archive/sample-renewal/chain1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem rename to certbot/tests/testdata/archive/sample-renewal/chain1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem b/certbot/tests/testdata/archive/sample-renewal/fullchain1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem rename to certbot/tests/testdata/archive/sample-renewal/fullchain1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem b/certbot/tests/testdata/archive/sample-renewal/privkey1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem rename to certbot/tests/testdata/archive/sample-renewal/privkey1.pem diff --git a/letsencrypt/tests/testdata/cert-san.pem b/certbot/tests/testdata/cert-san.pem similarity index 100% rename from letsencrypt/tests/testdata/cert-san.pem rename to certbot/tests/testdata/cert-san.pem diff --git a/letsencrypt/tests/testdata/cert.b64jose b/certbot/tests/testdata/cert.b64jose similarity index 100% rename from letsencrypt/tests/testdata/cert.b64jose rename to certbot/tests/testdata/cert.b64jose diff --git a/letsencrypt/tests/testdata/cert.der b/certbot/tests/testdata/cert.der similarity index 100% rename from letsencrypt/tests/testdata/cert.der rename to certbot/tests/testdata/cert.der diff --git a/letsencrypt/tests/testdata/cert.pem b/certbot/tests/testdata/cert.pem similarity index 100% rename from letsencrypt/tests/testdata/cert.pem rename to certbot/tests/testdata/cert.pem diff --git a/letsencrypt/tests/testdata/cli.ini b/certbot/tests/testdata/cli.ini similarity index 100% rename from letsencrypt/tests/testdata/cli.ini rename to certbot/tests/testdata/cli.ini diff --git a/letsencrypt/tests/testdata/csr-6sans.pem b/certbot/tests/testdata/csr-6sans.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-6sans.pem rename to certbot/tests/testdata/csr-6sans.pem diff --git a/letsencrypt/tests/testdata/csr-nosans.pem b/certbot/tests/testdata/csr-nosans.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-nosans.pem rename to certbot/tests/testdata/csr-nosans.pem diff --git a/letsencrypt/tests/testdata/csr-san.der b/certbot/tests/testdata/csr-san.der similarity index 100% rename from letsencrypt/tests/testdata/csr-san.der rename to certbot/tests/testdata/csr-san.der diff --git a/letsencrypt/tests/testdata/csr-san.pem b/certbot/tests/testdata/csr-san.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-san.pem rename to certbot/tests/testdata/csr-san.pem diff --git a/letsencrypt/tests/testdata/csr.der b/certbot/tests/testdata/csr.der similarity index 100% rename from letsencrypt/tests/testdata/csr.der rename to certbot/tests/testdata/csr.der diff --git a/letsencrypt/tests/testdata/csr.pem b/certbot/tests/testdata/csr.pem similarity index 100% rename from letsencrypt/tests/testdata/csr.pem rename to certbot/tests/testdata/csr.pem diff --git a/letsencrypt/tests/testdata/dsa512_key.pem b/certbot/tests/testdata/dsa512_key.pem similarity index 100% rename from letsencrypt/tests/testdata/dsa512_key.pem rename to certbot/tests/testdata/dsa512_key.pem diff --git a/letsencrypt/tests/testdata/dsa_cert.pem b/certbot/tests/testdata/dsa_cert.pem similarity index 100% rename from letsencrypt/tests/testdata/dsa_cert.pem rename to certbot/tests/testdata/dsa_cert.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/cert.pem b/certbot/tests/testdata/live/sample-renewal/cert.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/cert.pem rename to certbot/tests/testdata/live/sample-renewal/cert.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/chain.pem b/certbot/tests/testdata/live/sample-renewal/chain.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/chain.pem rename to certbot/tests/testdata/live/sample-renewal/chain.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem b/certbot/tests/testdata/live/sample-renewal/fullchain.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem rename to certbot/tests/testdata/live/sample-renewal/fullchain.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/privkey.pem b/certbot/tests/testdata/live/sample-renewal/privkey.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/privkey.pem rename to certbot/tests/testdata/live/sample-renewal/privkey.pem diff --git a/letsencrypt/tests/testdata/matching_cert.pem b/certbot/tests/testdata/matching_cert.pem similarity index 100% rename from letsencrypt/tests/testdata/matching_cert.pem rename to certbot/tests/testdata/matching_cert.pem diff --git a/letsencrypt/tests/testdata/rsa256_key.pem b/certbot/tests/testdata/rsa256_key.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa256_key.pem rename to certbot/tests/testdata/rsa256_key.pem diff --git a/letsencrypt/tests/testdata/rsa512_key.pem b/certbot/tests/testdata/rsa512_key.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa512_key.pem rename to certbot/tests/testdata/rsa512_key.pem diff --git a/letsencrypt/tests/testdata/rsa512_key_2.pem b/certbot/tests/testdata/rsa512_key_2.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa512_key_2.pem rename to certbot/tests/testdata/rsa512_key_2.pem diff --git a/letsencrypt/tests/testdata/sample-renewal-ancient.conf b/certbot/tests/testdata/sample-renewal-ancient.conf similarity index 100% rename from letsencrypt/tests/testdata/sample-renewal-ancient.conf rename to certbot/tests/testdata/sample-renewal-ancient.conf diff --git a/letsencrypt/tests/testdata/sample-renewal.conf b/certbot/tests/testdata/sample-renewal.conf similarity index 100% rename from letsencrypt/tests/testdata/sample-renewal.conf rename to certbot/tests/testdata/sample-renewal.conf diff --git a/letsencrypt/tests/testdata/webrootconftest.ini b/certbot/tests/testdata/webrootconftest.ini similarity index 100% rename from letsencrypt/tests/testdata/webrootconftest.ini rename to certbot/tests/testdata/webrootconftest.ini diff --git a/docker-compose.yml b/docker-compose.yml index dbe6e4f01..8b2a8e9a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,12 +3,12 @@ production: ports: - "443:443" -# For development, mount git root to /opt/letsencrypt/src in order to +# For development, mount git root to /opt/certbot/src in order to # make the dev workflow more vagrant-like. development: build: . ports: - "443:443" volumes: - - .:/opt/letsencrypt/src - - /opt/letsencrypt/venv + - .:/opt/certbot/src + - /opt/certbot/venv diff --git a/docs/api/continuity_auth.rst b/docs/api/continuity_auth.rst deleted file mode 100644 index 3276220f5..000000000 --- a/docs/api/continuity_auth.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`certbot.continuity_auth` ----------------------------------- - -.. automodule:: certbot.continuity_auth - :members: diff --git a/docs/api/proof_of_possession.rst b/docs/api/proof_of_possession.rst deleted file mode 100644 index 2e7642a45..000000000 --- a/docs/api/proof_of_possession.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`certbot.proof_of_possession` --------------------------------------- - -.. automodule:: certbot.proof_of_possession - :members: diff --git a/docs/conf.py b/docs/conf.py index 739d6ee43..fb2bdea73 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Let's Encrypt documentation build configuration file, created by +# Certbot documentation build configuration file, created by # sphinx-quickstart on Sun Nov 23 20:35:21 2014. # # This file is execfile()d with the current directory set to its @@ -21,7 +21,7 @@ import sys here = os.path.abspath(os.path.dirname(__file__)) # read version number (and other metadata) from package init -init_fn = os.path.join(here, '..', 'letsencrypt', '__init__.py') +init_fn = os.path.join(here, '..', 'certbot', '__init__.py') with codecs.open(init_fn, encoding='utf8') as fd: meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", fd.read())) @@ -277,9 +277,9 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'letsencrypt', u'Let\'s Encrypt Documentation', + ('index', 'certbot', u'Let\'s Encrypt Documentation', [project], 7), - ('man/letsencrypt', 'letsencrypt', u'letsencrypt script documentation', + ('man/certbot', 'certbot', u'certbot script documentation', [project], 1), ] diff --git a/docs/contributing.rst b/docs/contributing.rst deleted file mode 100644 index 5a9afd5c5..000000000 --- a/docs/contributing.rst +++ /dev/null @@ -1,432 +0,0 @@ -=============== -Developer Guide -=============== - -.. contents:: Table of Contents - :local: - - -.. _hacking: - -Hacking -======= - -Running a local copy of the client ----------------------------------- - -Running the client in developer mode from your local tree is a little -different than running ``certbot-auto``. To get set up, do these things -once: - -.. code-block:: shell - - git clone https://github.com/certbot/certbot - cd certbot - ./certbot-auto-source/certbot-auto --os-packages-only - ./tools/venv.sh - -Then in each shell where you're working on the client, do: - -.. code-block:: shell - - source ./venv/bin/activate - -After that, your shell will be using the virtual environment, and you run the -client by typing: - -.. code-block:: shell - - certbot - -Activating a shell in this way makes it easier to run unit tests -with ``tox`` and integration tests, as described below. To reverse this, you -can type ``deactivate``. More information can be found in the `virtualenv docs`_. - -.. _`virtualenv docs`: https://virtualenv.pypa.io - -Find issues to work on ----------------------- - -You can find the open issues in the `github issue tracker`_. Comparatively -easy ones are marked `Good Volunteer Task`_. If you're starting work on -something, post a comment to let others know and seek feedback on your plan -where appropriate. - -Once you've got a working branch, you can open a pull request. All changes in -your pull request must have thorough unit test coverage, pass our -`integration`_ tests, and be compliant with the :ref:`coding style -`. - -.. _github issue tracker: https://github.com/certbot/certbot/issues -.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 - -Testing -------- - -The following tools are there to help you: - -- ``tox`` starts a full set of tests. Please note that it includes - apacheconftest, which uses the system's Apache install to test config file - parsing, so it should only be run on systems that have an - experimental, non-production Apache2 install on them. ``tox -e - apacheconftest`` can be used to run those specific Apache conf tests. - -- ``tox -e py27``, ``tox -e py26`` etc, run unit tests for specific Python - versions. - -- ``tox -e cover`` checks the test coverage only. Calling the - ``./tox.cover.sh`` script directly (or even ``./tox.cover.sh $pkg1 - $pkg2 ...`` for any subpackages) might be a bit quicker, though. - -- ``tox -e lint`` checks the style of the whole project, while - ``pylint --rcfile=.pylintrc path`` will check a single file or - specific directory only. - -- For debugging, we recommend ``pip install ipdb`` and putting - ``import ipdb; ipdb.set_trace()`` statement inside the source - code. Alternatively, you can use Python's standard library `pdb`, - but you won't get TAB completion... - - -.. _integration: - -Integration testing with the boulder CA -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Generally it is sufficient to open a pull request and let Github and Travis run -integration tests for you. - -However, if you prefer to run tests, you can use Vagrant, using the Vagrantfile -in Certbot's repository. To execute the tests on a Vagrant box, the only -command you are required to run is:: - - ./tests/boulder-integration.sh - -Otherwise, please follow the following instructions. - -Mac OS X users: Run ``./tests/mac-bootstrap.sh`` instead of -``boulder-start.sh`` to install dependencies, configure the -environment, and start boulder. - -Otherwise, install `Go`_ 1.5, ``libtool-ltdl``, ``mariadb-server`` and -``rabbitmq-server`` and then start Boulder_, an ACME CA server. - -If you can't get packages of Go 1.5 for your Linux system, -you can execute the following commands to install it: - -.. code-block:: shell - - wget https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz -P /tmp/ - sudo tar -C /usr/local -xzf /tmp/go1.5.3.linux-amd64.tar.gz - if ! grep -Fxq "export GOROOT=/usr/local/go" ~/.profile ; then echo "export GOROOT=/usr/local/go" >> ~/.profile; fi - if ! grep -Fxq "export PATH=\\$GOROOT/bin:\\$PATH" ~/.profile ; then echo "export PATH=\\$GOROOT/bin:\\$PATH" >> ~/.profile; fi - -These commands download `Go`_ 1.5.3 to ``/tmp/``, extracts to ``/usr/local``, -and then adds the export lines required to execute ``boulder-start.sh`` to -``~/.profile`` if they were not previously added - -Make sure you execute the following command after `Go`_ finishes installing:: - - if ! grep -Fxq "export GOPATH=\\$HOME/go" ~/.profile ; then echo "export GOPATH=\\$HOME/go" >> ~/.profile; fi - -Afterwards, you'd be able to start Boulder_ using the following command:: - - ./tests/boulder-start.sh - -The script will download, compile and run the executable; please be -patient - it will take some time... Once its ready, you will see -``Server running, listening on 127.0.0.1:4000...``. Add ``/etc/hosts`` -entries pointing ``le.wtf``, ``le1.wtf``, ``le2.wtf``, ``le3.wtf`` -and ``nginx.wtf`` to 127.0.0.1. You may now run (in a separate terminal):: - - ./tests/boulder-integration.sh && echo OK || echo FAIL - -If you would like to test `certbot_nginx` plugin (highly -encouraged) make sure to install prerequisites as listed in -``certbot-nginx/tests/boulder-integration.sh`` and rerun -the integration tests suite. - -.. _Boulder: https://github.com/certbot/boulder -.. _Go: https://golang.org - - -Code components and layout -========================== - -acme - contains all protocol specific code -certbot - all client code - - -Plugin-architecture -------------------- - -Certbot has a plugin architecture to facilitate support for -different webservers, other TLS servers, and operating systems. -The interfaces available for plugins to implement are defined in -`interfaces.py`_ and `plugins/common.py`_. - -The most common kind of plugin is a "Configurator", which is likely to -implement the `~certbot.interfaces.IAuthenticator` and -`~certbot.interfaces.IInstaller` interfaces (though some -Configurators may implement just one of those). - -There are also `~certbot.interfaces.IDisplay` plugins, -which implement bindings to alternative UI libraries. - -.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py -.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 - - -Authenticators --------------- - -Authenticators are plugins designed to prove that this client deserves a -certificate for some domain name by solving challenges received from -the ACME server. From the protocol, there are essentially two -different types of challenges. Challenges that must be solved by -individual plugins in order to satisfy domain validation (subclasses -of `~.DVChallenge`, i.e. `~.challenges.TLSSNI01`, -`~.challenges.HTTP01`, `~.challenges.DNS`) and continuity specific -challenges (subclasses of `~.ContinuityChallenge`, -i.e. `~.challenges.RecoveryToken`, `~.challenges.RecoveryContact`, -`~.challenges.ProofOfPossession`). Continuity challenges are -always handled by the `~.ContinuityAuthenticator`, while plugins are -expected to handle `~.DVChallenge` types. -Right now, we have two authenticator plugins, the `~.ApacheConfigurator` -and the `~.StandaloneAuthenticator`. The Standalone and Apache -authenticators only solve the `~.challenges.TLSSNI01` challenge currently. -(You can set which challenges your authenticator can handle through the -:meth:`~.IAuthenticator.get_chall_pref`. - -(FYI: We also have a partial implementation for a `~.DNSAuthenticator` -in a separate branch). - - -Installer ---------- - -Installers plugins exist to actually setup the certificate in a server, -possibly tweak the security configuration to make it more correct and secure -(Fix some mixed content problems, turn on HSTS, redirect to HTTPS, etc). -Installer plugins tell the main client about their abilities to do the latter -via the :meth:`~.IInstaller.supported_enhancements` call. We currently -have two Installers in the tree, the `~.ApacheConfigurator`. and the -`~.NginxConfigurator`. External projects have made some progress toward -support for IIS, Icecast and Plesk. - -Installers and Authenticators will oftentimes be the same class/object -(because for instance both tasks can be performed by a webserver like nginx) -though this is not always the case (the standalone plugin is an authenticator -that listens on port 443, but it cannot install certs; a postfix plugin would -be an installer but not an authenticator). - -Installers and Authenticators are kept separate because -it should be possible to use the `~.StandaloneAuthenticator` (it sets -up its own Python server to perform challenges) with a program that -cannot solve challenges itself (Such as MTA installers). - - -Installer Development ---------------------- - -There are a few existing classes that may be beneficial while -developing a new `~certbot.interfaces.IInstaller`. -Installers aimed to reconfigure UNIX servers may use Augeas for -configuration parsing and can inherit from `~.AugeasConfigurator` class -to handle much of the interface. Installers that are unable to use -Augeas may still find the `~.Reverter` class helpful in handling -configuration checkpoints and rollback. - - -Display -~~~~~~~ - -We currently offer a pythondialog and "text" mode for displays. Display -plugins implement the `~certbot.interfaces.IDisplay` -interface. - -.. _dev-plugin: - -Writing your own plugin -======================= - -Certbot supports dynamic discovery of plugins through the -`setuptools entry points`_. This way you can, for example, create a -custom implementation of `~certbot.interfaces.IAuthenticator` or -the `~certbot.interfaces.IInstaller` without having to merge it -with the core upstream source code. An example is provided in -``examples/plugins/`` directory. - -.. warning:: Please be aware though that as this client is still in a - developer-preview stage, the API may undergo a few changes. If you - believe the plugin will be beneficial to the community, please - consider submitting a pull request to the repo and we will update - it with any necessary API changes. - -.. _`setuptools entry points`: - https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins - - -.. _coding-style: - -Coding style -============ - -Please: - -1. **Be consistent with the rest of the code**. - -2. Read `PEP 8 - Style Guide for Python Code`_. - -3. Follow the `Google Python Style Guide`_, with the exception that we - use `Sphinx-style`_ documentation:: - - def foo(arg): - """Short description. - - :param int arg: Some number. - - :returns: Argument - :rtype: int - - """ - return arg - -4. Remember to use ``pylint``. - -.. _Google Python Style Guide: - https://google-styleguide.googlecode.com/svn/trunk/pyguide.html -.. _Sphinx-style: http://sphinx-doc.org/ -.. _PEP 8 - Style Guide for Python Code: - https://www.python.org/dev/peps/pep-0008 - -Submitting a pull request -========================= - -Steps: - -1. Write your code! -2. Make sure your environment is set up properly and that you're in your - virtualenv. You can do this by running ``./tools/venv.sh``. - (this is a **very important** step) -3. Run ``./pep8.travis.sh`` to do a cursory check of your code style. - Fix any errors. -4. Run ``tox -e lint`` to check for pylint errors. Fix any errors. -5. Run ``tox`` to run the entire test suite including coverage. Fix any errors. -6. If your code touches communication with an ACME server/Boulder, you - should run the integration tests, see `integration`_. See `Known Issues`_ - for some common failures that have nothing to do with your code. -7. Submit the PR. -8. Did your tests pass on Travis? If they didn't, it might not be your fault! - See `Known Issues`_. If it's not a known issue, fix any errors. - -.. _Known Issues: - https://github.com/certbot/certbot/wiki/Known-issues - -Updating the documentation -========================== - -In order to generate the Sphinx documentation, run the following -commands: - -.. code-block:: shell - - make -C docs clean html - -This should generate documentation in the ``docs/_build/html`` -directory. - - -Other methods for running the client -==================================== - -Vagrant -------- - -If you are a Vagrant user, Certbot comes with a Vagrantfile that -automates setting up a development environment in an Ubuntu 14.04 -LTS VM. To set it up, simply run ``vagrant up``. The repository is -synced to ``/vagrant``, so you can get started with: - -.. code-block:: shell - - vagrant ssh - cd /vagrant - sudo ./venv/bin/certbot - -Support for other Linux distributions coming soon. - -.. note:: - Unfortunately, Python distutils and, by extension, setup.py and - tox, use hard linking quite extensively. Hard linking is not - supported by the default sync filesystem in Vagrant. As a result, - all actions with these commands are *significantly slower* in - Vagrant. One potential fix is to `use NFS`_ (`related issue`_). - -.. _use NFS: http://docs.vagrantup.com/v2/synced-folders/nfs.html -.. _related issue: https://github.com/ClusterHQ/flocker/issues/516 - - -Docker ------- - -OSX users will probably find it easiest to set up a Docker container for -development. Certbot comes with a Dockerfile (``Dockerfile-dev``) -for doing so. To use Docker on OSX, install and setup docker-machine using the -instructions at https://docs.docker.com/installation/mac/. - -To build the development Docker image:: - - docker build -t certbot -f Dockerfile-dev . - -Now run tests inside the Docker image: - -.. code-block:: shell - - docker run -it certbot bash - cd src - tox -e py27 - - -.. _prerequisites: - -Notes on OS dependencies -======================== - -OS-level dependencies can be installed like so: - -.. code-block:: shell - - certbot-auto-source/certbot-auto --os-packages-only - -In general... - -* ``sudo`` is required as a suggested way of running privileged process -* `Python`_ 2.6/2.7 is required -* `Augeas`_ is required for the Python bindings -* ``virtualenv`` and ``pip`` are used for managing other python library - dependencies - -.. _Python: https://wiki.python.org/moin/BeginnersGuide/Download -.. _Augeas: http://augeas.net/ -.. _Virtualenv: https://virtualenv.pypa.io - - -Debian ------- - -For squeeze you will need to: - -- Use ``virtualenv --no-site-packages -p python`` instead of ``-p python2``. - - -FreeBSD -------- - -Package installation for FreeBSD uses ``pkg``, not ports. - -FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see -below), you will need a compatible shell, e.g. ``pkg install bash && -bash``. diff --git a/examples/cli.ini b/examples/cli.ini index f0c993c57..63af3cc49 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -1,5 +1,5 @@ # This is an example of the kind of things you can do in a configuration file. -# All flags used by the client can be configured here. Run Let's Encrypt with +# All flags used by the client can be configured here. Run Certbot with # "--help" to learn more about the available options. # Use a 4096 bit RSA key instead of 2048 diff --git a/examples/generate-csr.sh b/examples/generate-csr.sh index c4a3af016..55f6c7b9f 100755 --- a/examples/generate-csr.sh +++ b/examples/generate-csr.sh @@ -25,4 +25,4 @@ SAN="$domains" openssl req -config "${OPENSSL_CNF:-openssl.cnf}" \ -outform DER # 512 or 1024 too low for Boulder, 2048 is smallest for tests -echo "You can now run: letsencrypt auth --csr ${CSR_PATH:-csr.der}" +echo "You can now run: certbot auth --csr ${CSR_PATH:-csr.der}" diff --git a/examples/plugins/letsencrypt_example_plugins.py b/examples/plugins/certbot_example_plugins.py similarity index 82% rename from examples/plugins/letsencrypt_example_plugins.py rename to examples/plugins/certbot_example_plugins.py index 5c22ca7ff..9dec2e108 100644 --- a/examples/plugins/letsencrypt_example_plugins.py +++ b/examples/plugins/certbot_example_plugins.py @@ -1,12 +1,12 @@ -"""Example Let's Encrypt plugins. +"""Example Certbot plugins. -For full examples, see `letsencrypt.plugins`. +For full examples, see `certbot.plugins`. """ import zope.interface -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import interfaces +from certbot.plugins import common @zope.interface.implementer(interfaces.IAuthenticator) diff --git a/examples/plugins/setup.py b/examples/plugins/setup.py index 71bb95333..4538e83b8 100644 --- a/examples/plugins/setup.py +++ b/examples/plugins/setup.py @@ -2,16 +2,16 @@ from setuptools import setup setup( - name='letsencrypt-example-plugins', - package='letsencrypt_example_plugins.py', + name='certbot-example-plugins', + package='certbot_example_plugins.py', install_requires=[ - 'letsencrypt', + 'certbot', 'zope.interface', ], entry_points={ - 'letsencrypt.plugins': [ - 'example_authenticator = letsencrypt_example_plugins:Authenticator', - 'example_installer = letsencrypt_example_plugins:Installer', + 'certbot.plugins': [ + 'example_authenticator = certbot_example_plugins:Authenticator', + 'example_installer = certbot_example_plugins:Installer', ], }, ) diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in index bdb67199f..97e2ad3df 100644 --- a/letsencrypt-apache/MANIFEST.in +++ b/letsencrypt-apache/MANIFEST.in @@ -1,7 +1,2 @@ include LICENSE.txt include README.rst -recursive-include docs * -recursive-include letsencrypt_apache/tests/testdata * -include letsencrypt_apache/centos-options-ssl-apache.conf -include letsencrypt_apache/options-ssl-apache.conf -recursive-include letsencrypt_apache/augeas_lens *.aug diff --git a/letsencrypt-apache/README.rst b/letsencrypt-apache/README.rst index 3505fd594..c0c201f14 100644 --- a/letsencrypt-apache/README.rst +++ b/letsencrypt-apache/README.rst @@ -1 +1,2 @@ -Apache plugin for Let's Encrypt client +This package is a simple shim for backwards compatibility around +``certbot-apache``, the Apache plugin for ``certbot``. diff --git a/letsencrypt-apache/docs/api/augeas_configurator.rst b/letsencrypt-apache/docs/api/augeas_configurator.rst deleted file mode 100644 index 3b1821e3d..000000000 --- a/letsencrypt-apache/docs/api/augeas_configurator.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.augeas_configurator` ---------------------------------------------- - -.. automodule:: letsencrypt_apache.augeas_configurator - :members: diff --git a/letsencrypt-apache/docs/api/configurator.rst b/letsencrypt-apache/docs/api/configurator.rst deleted file mode 100644 index 2ed613286..000000000 --- a/letsencrypt-apache/docs/api/configurator.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.configurator` --------------------------------------- - -.. automodule:: letsencrypt_apache.configurator - :members: diff --git a/letsencrypt-apache/docs/api/display_ops.rst b/letsencrypt-apache/docs/api/display_ops.rst deleted file mode 100644 index 59ff9d15e..000000000 --- a/letsencrypt-apache/docs/api/display_ops.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.display_ops` -------------------------------------- - -.. automodule:: letsencrypt_apache.display_ops - :members: diff --git a/letsencrypt-apache/docs/api/obj.rst b/letsencrypt-apache/docs/api/obj.rst deleted file mode 100644 index 969293ca1..000000000 --- a/letsencrypt-apache/docs/api/obj.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.obj` ------------------------------ - -.. automodule:: letsencrypt_apache.obj - :members: diff --git a/letsencrypt-apache/docs/api/parser.rst b/letsencrypt-apache/docs/api/parser.rst deleted file mode 100644 index 0c998e06c..000000000 --- a/letsencrypt-apache/docs/api/parser.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.parser` --------------------------------- - -.. automodule:: letsencrypt_apache.parser - :members: diff --git a/letsencrypt-apache/docs/api/tls_sni_01.rst b/letsencrypt-apache/docs/api/tls_sni_01.rst deleted file mode 100644 index 2c11a3394..000000000 --- a/letsencrypt-apache/docs/api/tls_sni_01.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.tls_sni_01` ------------------------------------- - -.. automodule:: letsencrypt_apache.tls_sni_01 - :members: diff --git a/letsencrypt-apache/letsencrypt_apache/__init__.py b/letsencrypt-apache/letsencrypt_apache/__init__.py index c0d1e0d52..cc8faef21 100644 --- a/letsencrypt-apache/letsencrypt_apache/__init__.py +++ b/letsencrypt-apache/letsencrypt_apache/__init__.py @@ -1 +1,8 @@ """Let's Encrypt Apache plugin.""" +import sys + + +import certbot_apache + + +sys.modules['letsencrypt_apache'] = certbot_apache diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/README b/letsencrypt-apache/letsencrypt_apache/augeas_lens/README deleted file mode 100644 index f801efd43..000000000 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/README +++ /dev/null @@ -1,2 +0,0 @@ -Let's Encrypt includes the very latest Augeas lenses in order to ship bug fixes -to Apache configuration handling bugs as quickly as possible diff --git a/letsencrypt-apache/letsencrypt_apache/tests/__init__.py b/letsencrypt-apache/letsencrypt_apache/tests/__init__.py deleted file mode 100644 index 2c0849a3d..000000000 --- a/letsencrypt-apache/letsencrypt_apache/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt Apache Tests""" diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt deleted file mode 100644 index b51956b0c..000000000 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt +++ /dev/null @@ -1,6 +0,0 @@ -Issues for which some kind of test case should be constructable, but we do not -currently have one: - -https://github.com/letsencrypt/letsencrypt/issues/1213 -https://github.com/letsencrypt/letsencrypt/issues/1602 - diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf deleted file mode 120000 index f31102913..000000000 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf +++ /dev/null @@ -1 +0,0 @@ -../sites-available/letsencrypt.conf \ No newline at end of file diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 46f4da54c..a52044f87 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -1,39 +1,38 @@ +import codecs +import os import sys from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() -# Please update tox.ini when modifying dependency version requirements + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around certbot-apache install_requires = [ - 'acme=={0}'.format(version), + 'certbot-apache', 'letsencrypt=={0}'.format(version), - 'python-augeas', - # For pkg_resources. >=1.0 so pip resolves it to a version cryptography - # will tolerate; see #2599: - 'setuptools>=1.0', - 'zope.component', - 'zope.interface', ] -if sys.version_info < (2, 7): - install_requires.append('mock<1.1.0') -else: - install_requires.append('mock') - -docs_extras = [ - 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags - 'sphinx_rtd_theme', -] setup( name='letsencrypt-apache', version=version, - description="Apache plugin for Let's Encrypt client", + description="Apache plugin for Let's Encrypt", + long_description=readme, url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ @@ -57,13 +56,4 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, - extras_require={ - 'docs': docs_extras, - }, - entry_points={ - 'letsencrypt.plugins': [ - 'apache = letsencrypt_apache.configurator:ApacheConfigurator', - ], - }, - test_suite='letsencrypt_apache', ) diff --git a/letsencrypt-auto b/letsencrypt-auto index 8dda5f183..942fd8ea2 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -19,7 +19,7 @@ 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" -LE_AUTO_VERSION="0.4.2" +LE_AUTO_VERSION="0.5.0" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support @@ -348,28 +348,45 @@ BootstrapFreeBsd() { } BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } @@ -411,7 +428,7 @@ Bootstrap() { else echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo - echo "You will need to bootstrap, configure virtualenv, and run a peep install manually." + echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" echo "for more info." fi @@ -421,19 +438,6 @@ TempDir() { mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X } -InstallRequirements() { - set +e - PEEP_OUT=`"$VENV_BIN/python" "$TEMP_DIR/peep.py" install -r "$TEMP_DIR/$1"` - PEEP_STATUS=$? - set -e - if [ "$PEEP_STATUS" != 0 ]; then - # Report error. (Otherwise, be quiet.) - echo "Had a problem while downloading and verifying Python packages:" - echo "$PEEP_OUT" - rm -rf "$VENV_PATH" - exit 1 - fi -} if [ "$1" = "--le-auto-phase2" ]; then @@ -441,7 +445,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -457,255 +462,214 @@ if [ "$1" = "--le-auto-phase2" ]; then echo "Installing Python packages..." TEMP_DIR=$(TempDir) - trap "rm -rf '$TEMP_DIR'" EXIT # There is no $ interpolation due to quotes on starting heredoc delimiter. - # ------------------------------------------------------------------------- - cat << "UNLIKELY_EOF" > "$TEMP_DIR/setuptools-requirements.txt" -# cryptography requires a more modern version of setuptools. -# sha256: _ANFf7h6utSdwJ-cMTOGNpPn3bbKgrtQpzmnc3nOWpo -# sha256: JPz8FTZKn-CaIg830tztyEl5Xj3j5LOT7piOZqnL2Fo -# sha256: gJaELiTE8ddN_xKr6Qwm0S8F0NmlbtXgb8qm-qHkC2o -setuptools==20.2.2 - -UNLIKELY_EOF # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" # This is the flattened list of packages letsencrypt-auto installs. To generate # this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and # then use `hashin` or a more secure method to gather the hashes. -# sha256: wxZH7baf09RlqEfqMVfTe-0flfGXYLEaR6qRwEtmYxQ -# sha256: YrCJpVvh2JSc0rx-DfC9254Cj678jDIDjMhIYq791uQ -argparse==1.4.0 +argparse==1.4.0 \ + --hash=sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314 \ + --hash=sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4 # This comes before cffi because cffi will otherwise install an unchecked # version via setup_requires. -# sha256: eVm0p0q9wnsxL-0cIebK-TCc4LKeqGtZH9Lpns3yf3M -pycparser==2.14 +pycparser==2.14 \ + --hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73 -# sha256: U8HJ3bMEMVE-t_PN7wo-BrDxJSGIqqd0SvD1pM1F268 -# sha256: pWj0nfyhKo2fNwGHJX78WKOBCeHu5xTZKFYdegGKZPg -# sha256: gJxsqM-8ruv71DK0V2ABtA04_yRjdzy1dXfXXhoCC8M -# sha256: hs3KLNnLpBQiIwOQ3xff6qnzRKkR45dci-naV7NVSOk -# sha256: JLE9uErsOFyiPHuN7YPvi7QXe8GB0UdY-fl1vl0CDYY -# sha256: lprv_XwOCX9r4e_WgsFWriJlkaB5OpS2wtXkKT9MjU4 -# sha256: AA81jUsPokn-qrnBzn1bL-fgLnvfaAbCZBhQX8aF4mg -# sha256: qdhvRgu9g1ii1ROtd54_P8h447k6ALUAL66_YW_-a5w -# sha256: MSezqzPrI8ysBx-aCAJ0jlz3xcvNAkgrsGPjW0HbsLA -# sha256: 4rLUIjZGmkAiTTnntsYFdfOIsvQj81TH7pClt_WMgGU -# sha256: jC3Mr-6JsbQksL7GrS3ZYiyUnSAk6Sn12h7YAerHXx0 -# sha256: pN56TRGu1Ii6tPsU9JiFh6gpvs5aIEM_eA1uM7CAg8s -# sha256: XKj-MEJSZaSSdOSwITobyY9LE0Sa5elvmEdx5dg-WME -# sha256: pP04gC9Z5xTrqBoCT2LbcQsn2-J6fqEukRU3MnqoTTA -# sha256: hs1pErvIPpQF1Kc81_S07oNTZS0tvHyCAQbtW00bqzo -# sha256: jx0XfTZOo1kAQVriTKPkcb49UzTtBBkpQGjEn0WROZg -cffi==1.4.2 +cffi==1.4.2 \ + --hash=sha256:53c1c9ddb30431513eb7f3cdef0a3e06b0f1252188aaa7744af0f5a4cd45dbaf \ + --hash=sha256:a568f49dfca12a8d9f370187257efc58a38109e1eee714d928561d7a018a64f8 \ + --hash=sha256:809c6ca8cfbcaeebfbd432b4576001b40d38ff2463773cb57577d75e1a020bc3 \ + --hash=sha256:86cdca2cd9cba41422230390df17dfeaa9f344a911e3975c8be9da57b35548e9 \ + --hash=sha256:24b13db84aec385ca23c7b8ded83ef8bb4177bc181d14758f9f975be5d020d86 \ + --hash=sha256:969aeffd7c0e097f6be1efd682c156ae226591a0793a94b6c2d5e4293f4c8d4e \ + --hash=sha256:000f358d4b0fa249feaab9c1ce7d5b2fe7e02e7bdf6806c26418505fc685e268 \ + --hash=sha256:a9d86f460bbd8358a2d513ad779e3f3fc878e3b93a00b5002faebf616ffe6b9c \ + --hash=sha256:3127b3ab33eb23ccac071f9a0802748e5cf7c5cbcd02482bb063e35b41dbb0b0 \ + --hash=sha256:e2b2d42236469a40224d39e7b6c60575f388b2f423f354c7ee90a5b7f58c8065 \ + --hash=sha256:8c2dccafee89b1b424b0bec6ad2dd9622c949d2024e929f5da1ed801eac75f1d \ + --hash=sha256:a4de7a4d11aed488bab4fb14f4988587a829bece5a20433f780d6e33b08083cb \ + --hash=sha256:5ca8fe30425265a49274e4b0213a1bc98f4b13449ae5e96f984771e5d83e58c1 \ + --hash=sha256:a4fd38802f59e714eba81a024f62db710b27dbe27a7ea12e911537327aa84d30 \ + --hash=sha256:86cd6912bbc83e9405d4a73cd7f4b4ee8353652d2dbc7c820106ed5b4d1bab3a \ + --hash=sha256:8f1d177d364ea35900415ae24ca3e471be3d5334ed0419294068c49f45913998 +ConfigArgParse==0.10.0 \ + --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +configobj==5.0.6 \ + --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 +cryptography==1.2.3 \ + --hash=sha256:031938f73a5c5eb3e809e18ff7caeb6865351871417be6050cb8c86a9a202b9a \ + --hash=sha256:a179a38d50f8d68b491d7a313db78f8cabe290842cecddddc7b34d408e59db0a \ + --hash=sha256:906c88b2aadcf99cfabb24098263d1bf65ab0c8688acde10dae1f09d865920f1 \ + --hash=sha256:6e706c5c6088770b1d1b634e959e21963e315b0255f5f4777125ad3d54082977 \ + --hash=sha256:f5ebf8e31c48f8707921dca0e994de77813a9c9b9bf03c119c5ddf97bdcffe73 \ + --hash=sha256:c7b89e42288cc7fbee3812e99ef5c744f22452e11d6822f6807afc6d6b3be83e \ + --hash=sha256:8408d29865947109d8b68f1837a7cde1aa4dc86e0f79ca3ba58c0c44e443d6a5 \ + --hash=sha256:c7e76cf3c3d925dd31fa238cfb806cffba718c0f08707d77a538768477969956 \ + --hash=sha256:7d8de35380f31702758b7753bb5c40723832c73006dedb2f9099bf61a37f7287 \ + --hash=sha256:5edbee71fae5469ee83fe0a37866b9398c8ce3a46325c24fcedfbf097bb48a19 \ + --hash=sha256:594edafe4801c13bdc1cc305e7704a90c19617e95936f6ab457ee4ffe000ba50 \ + --hash=sha256:b7fdb16a0a7f481be42da744bfe1ea2163025de21f90f2c688a316f3c354da9c \ + --hash=sha256:207b8bf0fe0907336df38b733b487521cf9e138189aba9234ad54fe545dd0db8 \ + --hash=sha256:509a2f05386270cf783993c90d49ffefb3dd62aee45bf1ea8ce3d2cde7271c21 \ + --hash=sha256:ac69b65dd1af0179ede40c9f15788c88f73e628ea6c0519de3838e279bb388c6 \ + --hash=sha256:8df6fad6c6ae12fd7004ea29357f0a2b4d3774eaeca7656530d08d2d90cd41aa \ + --hash=sha256:0b8b96dd81cc1533a04f30382c0fe21c1972e189f794d0c4261a18cec08fd9b5 \ + --hash=sha256:cae8fca1883f23c50ea78d89de6fe4fefdb4cea83177760f47177559414ded93 \ + --hash=sha256:1a471ca576a9cdce1b1cd9f3a22b1d09ee44d46862037557de17919c0db44425 \ + --hash=sha256:8ec4e8e3d453b3a1b63b5f57737a434dcf1ee4a2f26f6ff7c5a37c3f679104d2 \ + --hash=sha256:8eb11c77dd8e73f48df6b2f7a7e16173fe0fe8fdfe266232832e88477e08454e +enum34==1.1.2 \ + --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ + --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 +funcsigs==0.4 \ + --hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \ + --hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033 +idna==2.0 \ + --hash=sha256:9b2fc50bd3c4ba306b9651b69411ef22026d4d8335b93afc2214cef1246ce707 \ + --hash=sha256:16199aad938b290f5be1057c0e1efc6546229391c23cea61ca940c115f7d3d3b +ipaddress==1.0.16 \ + --hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \ + --hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0 +linecache2==1.0.0 \ + --hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \ + --hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c +ndg-httpsclient==0.4.0 \ + --hash=sha256:e8c155fdebd9c4bcb0810b4ed01ae1987554b1ee034dd7532d7b8fdae38a6274 +ordereddict==1.1 \ + --hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f +parsedatetime==2.1 \ + --hash=sha256:ce9d422165cf6e963905cd5f74f274ebf7cc98c941916169178ef93f0e557838 \ + --hash=sha256:17c578775520c99131634e09cfca5a05ea9e1bd2a05cd06967ebece10df7af2d +pbr==1.8.1 \ + --hash=sha256:46c8db75ae75a056bd1cc07fa21734fe2e603d11a07833ecc1eeb74c35c72e0c \ + --hash=sha256:e2127626a91e6c885db89668976db31020f0af2da728924b56480fc7ccf09649 +psutil==3.3.0 \ + --hash=sha256:584f0b29fcc5d523b433cb8918b2fc74d67e30ee0b44a95baf031528f424619f \ + --hash=sha256:28ca0b6e9d99aa8dc286e8747a4471362b69812a25291de29b6a8d70a1545a0d \ + --hash=sha256:167ad5fff52a672c4ddc1c1a0b25146d6813ebb08a9aab0a3ac45f8a5b669c3b \ + --hash=sha256:e6dea6173a988727bb223d3497349ad5cdef5c0b282eff2d83e5f9065c53f85f \ + --hash=sha256:2af5e0a4aad66049955d0734aa4e3dc8caa17a9eaf8b4c1a27a5f1ee6e40f6fc \ + --hash=sha256:d9884dc0dc2e55e2448e495778dc9899c1c8bf37aeb2f434c1bea74af93c2683 \ + --hash=sha256:e27c2fe6dfcc8738be3d2c5a022f785eb72971057e1a9e1e34fba73bce8a71a6 \ + --hash=sha256:65afd6fecc8f3aed09ee4be63583bc8eb472f06ceaa4fe24c4d1d5a1a3c0e13f \ + --hash=sha256:ba1c558fbfcdf94515c2394b1155c1dc56e2bc2a9c17d30349827c9ed8a67e46 \ + --hash=sha256:ba95ea0022dcb64d36f0c1335c0605fae35bdf3e0fea8d92f5d0f6456a35e55b \ + --hash=sha256:421b6591d16b509aaa8d8c15821d66bb94cb4a8dc4385cad5c51b85d4a096d85 \ + --hash=sha256:326b305cbdb6f94dafbfe2c26b11da88b0ab07b8a07f8188ab9d75ff0c6e841a \ + --hash=sha256:9aede5b2b6fe46b3748ea8e5214443890d1634027bef3d33b7dad16556830278 \ + --hash=sha256:73bed1db894d1aa9c3c7e611d302cdeab7ae8a0dc0eeaf76727878db1ac5cd87 \ + --hash=sha256:935b5dd6d558af512f42501a7c08f41d7aff139af1bb3959daa3abb859234d6c \ + --hash=sha256:4ca0111cf157dcc0f2f69a323c5b5478718d68d45fc9435d84be0ec0f186215b \ + --hash=sha256:b6f13c95398a3fcf0226c4dcfa448560ba5865259cd96ec2810658651e932189 \ + --hash=sha256:ee6be30d1635bbdea4c4325d507dc8a0dbbde7e1c198bd62ddb9f43198b9e214 \ + --hash=sha256:dfa786858c268d7fbbe1b6175e001ec02738d7cfae0a7ce77bf9b651af676729 \ + --hash=sha256:aa77f9de72af9c16cc288cd4a24cf58824388f57d7a81e400c4616457629870e \ + --hash=sha256:f500093357d04da8140d87932cac2e54ef592a54ca8a743abb2850f60c2c22eb +pyasn1==0.1.9 \ + --hash=sha256:61f9d99e3cef65feb1bfe3a2eef7a93eb93819d345bf54bcd42f4e63d5204dae \ + --hash=sha256:1802a6dd32045e472a419db1441aecab469d33e0d2749e192abdec52101724af \ + --hash=sha256:35025cd9422c96504912f04e2f15fe79390a8597b430c2ca5d0534cf9309ffa0 \ + --hash=sha256:2f96ed5a0c329ca16230b326ca12b7461ec8f65e0be3e4f997516f36bf82a345 \ + --hash=sha256:28fee44217991cfad9e6a0b9f7e3f26041e21ebc96629e94e585ccd05d49fa65 \ + --hash=sha256:326e7a854a17fab07691204747695f8f692d674588a355c441fb14f660bf4e68 \ + --hash=sha256:cda5a90485709ca6795c86056c3e5fe7266028b05e53f1d527fdf93a6365a6b8 \ + --hash=sha256:0cb2a14742b543fdd68f931a14ce3829186ed2b1b2267a06787388c96b2dd9be \ + --hash=sha256:5191ff6b9126d2c039dd87f8ff025bed274baf07fa78afa46f556b1ad7265d6e \ + --hash=sha256:8323e03637b2d072cc7041300bac6ec448c3c28950ab40376036788e9a1af629 \ + --hash=sha256:853cacd96d1f701ddd67aa03ecc05f51890135b7262e922710112f12a2ed2a7f +pyOpenSSL==0.15.1 \ + --hash=sha256:88e45e6bb25dfed272a1ef2e728461d44b634c2cd689e989b6e56a349c5a3ae5 \ + --hash=sha256:f0a26070d6db0881de8bcc7846934b7c3c930d8f9c79d45883ee48984bc0d672 +pyRFC3339==1.0 \ + --hash=sha256:eea31835c56e2096af4363a5745a784878a61d043e247d3a6d6a0a32a9741f56 \ + --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 +python-augeas==0.5.0 \ + --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 +python2-pythondialog==3.3.0 \ + --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ + --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa +pytz==2015.7 \ + --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ + --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ + --hash=sha256:ead4aefa7007249e05e51b01095719d5a8dd95760089f5730aac5698b1932918 \ + --hash=sha256:3cca0df08bd0ed98432390494ce3ded003f5e661aa460be7a734bffe35983605 \ + --hash=sha256:3ede470d3d17ba3c07638dfa0d10452bc1b6e5ad326127a65ba77e6aaeb11bec \ + --hash=sha256:68c47964f7186eec306b13629627722b9079cd4447ed9e5ecaecd4eac84ca734 \ + --hash=sha256:dd5d3991950aae40a6c81de1578942e73d629808cefc51d12cd157980e6cfc18 \ + --hash=sha256:a77c52062c07eb7c7b30545dbc73e32995b7e117eea750317b5cb5c7a4618f14 \ + --hash=sha256:81af9aec4bc960a9a0127c488f18772dae4634689233f06f65443e7b11ebeb51 \ + --hash=sha256:e079b1dadc5c06246cc1bb6fe1b23a50b1d1173f2edd5104efd40bb73a28f406 \ + --hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \ + --hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \ + --hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3 +requests==2.9.1 \ + --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \ + --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f +six==1.10.0 \ + --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \ + --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a +traceback2==1.4.0 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 +unittest2==1.1.0 \ + --hash=sha256:13f77d0875db6d9b435e1d4f41e74ad4cc2eb6e1d5c824996092b3430f088bb8 \ + --hash=sha256:22882a0e418c284e1f718a822b3b022944d53d2d908e1690b319a9d3eb2c0579 +zope.component==4.2.2 \ + --hash=sha256:282c112b55dd8e3c869a3571f86767c150ab1284a9ace2bdec226c592acaf81a +zope.event==4.1.0 \ + --hash=sha256:dc7a59a2fd91730d3793131a5d261b29e93ec4e2a97f1bc487ce8defee2fe786 +zope.interface==4.1.3 \ + --hash=sha256:f07b631f7a601cd8cbd3332d54f43142c7088a83299f859356f08d1d4d4259b3 \ + --hash=sha256:de5cca083b9439d8002fb76bbe6b4998c5a5a721fab25b84298967f002df4c94 \ + --hash=sha256:6788416f7ea7f5b8a97be94825377aa25e8bdc73463e07baaf9858b29e737077 \ + --hash=sha256:6f3230f7254518201e5a3708cbb2de98c848304f06e3ded8bfb39e5825cba2e1 \ + --hash=sha256:5fa575a5240f04200c3088427d0d4b7b737f6e9018818a51d8d0f927a6a2517a \ + --hash=sha256:522194ad6a545735edd75c8a83f48d65d1af064e432a7d320d64f56bafc12e99 \ + --hash=sha256:e8c7b2d40943f71c99148c97f66caa7f5134147f57423f8db5b4825099ce9a09 \ + --hash=sha256:279024f0208601c3caa907c53876e37ad88625f7eaf1cb3842dbe360b2287017 \ + --hash=sha256:2e221a9eec7ccc58889a278ea13dcfed5ef939d80b07819a9a8b3cb1c681484f \ + --hash=sha256:69118965410ec86d44dc6b9017ee3ddbd582e0c0abeef62b3a19dbf6c8ad132b \ + --hash=sha256:d04df8686ec864d0cade8cf199f7f83aecd416109a20834d568f8310ded12dea \ + --hash=sha256:e75a947e15ee97e7e71e02ea302feb2fc62d3a2bb4668bf9dfbed43a506ac7e7 \ + --hash=sha256:4e45d22fb883222a5ab9f282a116fec5ee2e8d1a568ccff6a2d75bbd0eb6bcfc \ + --hash=sha256:bce9339bb3c7a55e0803b63d21c5839e8e479bc85c4adf42ae415b72f94facb2 \ + --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ + --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ + --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 +mock==1.0.1 \ + --hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \ + --hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc -# sha256: O1CoPdWBSd_O6Yy2VlJl0QtT6cCivKfu73-19VJIkKc -ConfigArgParse==0.10.0 +# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -# sha256: ovVlB3DhyH-zNa8Zqbfrc_wFzPIhROto230AzSvLCQI -configobj==5.0.6 - -# sha256: Axk49zpcXrPoCeGP98rraGU1GHFBe-YFDLjIapogK5o -# sha256: oXmjjVD41otJHXoxPbePjKvikIQs7N3dx7NNQI5Z2wo -# sha256: kGyIsqrc-Zz6uyQJgmPRv2WrDIaIrN4Q2uHwnYZZIPE -# sha256: bnBsXGCIdwsdG2NOlZ4hlj4xWwJV9fR3cSWtPVQIKXc -# sha256: 9ev44xxI-HB5Idyg6ZTed4E6nJub8DwRnF3fl73P_nM -# sha256: x7ieQiiMx_vuOBLpnvXHRPIkUuEdaCL2gHr8bWs76D4 -# sha256: hAjSmGWUcQnYto8YN6fN4apNyG4Peco7pYwMRORD1qU -# sha256: x-ds88PZJd0x-iOM-4Bs_7pxjA8IcH13pTh2hHeWmVY -# sha256: fY3jU4DzFwJ1i3dTu1xAcjgyxzAG3tsvkJm_YaN_coc -# sha256: XtvucfrlRp7oP-CjeGa5OYyM46RjJcJPzt-_CXu0ihk -# sha256: WU7a_kgBwTvcHMMF53BKkMGWF-lZNvarRX7k_-AAulA -# sha256: t_2xagp_SBvkLadEv-HqIWMCXeIfkPLGiKMW88NU2pw -# sha256: IHuL8P4JBzNt84tzO0h1Ic-eE4GJq6kjStVP5UXdDbg -# sha256: UJovBThicM94OZPJDUn_77PdYq7kW_HqjOPSzecnHCE -# sha256: rGm2XdGvAXnt5AyfFXiMiPc-Yo6mwFGd44OOJ5uziMY -# sha256: jfb61sauEv1wBOopNX8KK003dOrsp2VlMNCNLZDNQao -# sha256: C4uW3YHMFTOgTzA4LA_iHBly4Yn3lNDEJhoYzsCP2bU -# sha256: yuj8oYg_I8UOp42J3m_k_v20zqgxd3YPRxd1WUFN7ZM -# sha256: GkccpXapzc4bHNnzoisdCe5E1GhiA3VX3heRnA20RCU -# sha256: jsTo49RTs6G2O19Xc3pDTc8e5KLyb2_3xaN8P2eRBNI -# sha256: jrEcd92Oc_SN9rL3p-Fhc_4P6P3-JmIygy6IR34IRU4 -cryptography==1.2.3 - -# sha256: JHXX_N31lR6S_1RpcnWIAt5SYL9Akxmp8ZNOa7yLHcc -# sha256: NZB977D5krdat3iPZf7cHPIP-iJojg5vbxKvwGs-pQE -enum34==1.1.2 - -# sha256: _1rZ4vjZ5dHou_vPR3IqtSfPDVHK7u2dptD0B5k4P94 -# sha256: 2Dzm3wsOpmGHAP4ds1NSY5Goo62ht6ulL-16Ydp3IDM -funcsigs==0.4 - -# sha256: my_FC9PEujBrllG2lBHvIgJtTYM1uTr8IhTO8SRs5wc -# sha256: FhmarZOLKQ9b4QV8Dh78ZUYik5HCPOphypQMEV99PTs -idna==2.0 - -# sha256: k1cSgAzkdgcB2JrWd2Zs1SaR_S9vCzQMi0I5o8F5iKU -# sha256: WjGCsyKnBlJcRigspvBk0noCz_vUSfn0dBbx3JaqcbA -ipaddress==1.0.16 - -# sha256: 54vpwKDfy6xxL-BPv5K5bN2ugLG4QvJCSCFMhJbwBu8 -# sha256: Syb_TnEQ23butvWntkqCYjg51ZXCA47tpmLyott46Xw -linecache2==1.0.0 - -# sha256: 6MFV_evZxLywgQtO0BrhmHVUse4DTddTLXuP2uOKYnQ -ndg-httpsclient==0.4.0 - -# sha256: HDW0rCBs7y0kgWyJ-Jzyid09OM98RJuz-re_bUPwGx8 -ordereddict==1.1 - -# sha256: zp1CIWXPbpY5Bc1fdPJ06_fMmMlBkWFpF475Pw5VeDg -# sha256: F8V4d1UgyZExY04Jz8paBeqeG9KgXNBpZ-vs4Q33ry0 -parsedatetime==2.1 - -# sha256: Rsjbda51oFa9HMB_ohc0_i5gPRGgeDPswe63TDXHLgw -# sha256: 4hJ2JqkebIhduJZol22zECDwry2nKJJLVkgPx8zwlkk -pbr==1.8.1 - -# sha256: WE8LKfzF1SO0M8uJGLL8dNZ-MO4LRKlbrwMVKPQkYZ8 -# sha256: KMoLbp2Zqo3Chuh0ekRxNitpgSolKR3im2qNcKFUWg0 -# sha256: FnrV__UqZyxN3BwaCyUUbWgT67CKmqsKOsRfiltmnDs -# sha256: 5t6mFzqYhye7Ij00lzSa1c3vXAsoLv8tg-X5BlxT-F8 -# sha256: KvXgpKrWYEmVXQc0qk49yMqhep6vi0waJ6Xx7m5A9vw -# sha256: 2YhNwNwuVeJEjklXeNyYmcHIvzeusvQ0wb6nSvk8JoM -# sha256: 4nwv5t_Mhzi-PSxaAi94XrcpcQV-Gp4eNPunO86KcaY -# sha256: Za_W_syPOu0J7kvmNYO8jrRy8GzqpP4kxNHVoaPA4T8 -# sha256: uhxVj7_N-UUVwjlLEVXB3FbivCqcF9MDSYJ8ntimfkY -# sha256: upXqACLctk028MEzXAYF-uNb3z4P6o2S9dD2RWo15Vs -# sha256: QhtlkdFrUJqqjYwVgh1mu5TLSo3EOFytXFG4XUoJbYU -# sha256: MmswXL22-U2vv-LCaxHaiLCrB7igf4GIq511_wxuhBo -# sha256: mu3lsrb-RrN0jqjlIURDiQ0WNAJ77z0zt9rRZVaDAng -# sha256: c77R24lNGqnDx-YR0wLN6reuig3A7q92cnh42xrFzYc -# sha256: k1td1tVYr1EvQlAafAj0HXr_E5rxuzlZ2qOruFkjTWw -# sha256: TKARHPFX3MDy9poyPFtUeHGNaNRfyUNdhL4OwPGGIVs -# sha256: tvE8lTmKP88CJsTc-kSFYLpYZSWc2W7CgQZYZR6TIYk -# sha256: 7mvjDRY1u96kxDJdUH3IoNu95-HBmL1i3bn0MZi54hQ -# sha256: 36eGhYwmjX-74bYXXgAewCc418-uCnzne_m2Ua9nZyk -# sha256: qnf53nKvnBbMKIzUokz1iCQ4j1fXqB5ADEYWRXYphw4 -# sha256: 9QAJM1fQTagUDYeTLKwuVO9ZKlTKinQ6uyhQ9gwsIus -psutil==3.3.0 - -# sha256: YfnZnjzvZf6xv-Oi7vepPrk4GdNFv1S81C9OY9UgTa4 -# sha256: GAKm3TIEXkcqQZ2xRBrsq0adM-DSdJ4ZKr3sUhAXJK8 -# sha256: NQJc2UIsllBJEvBOLxX-eTkKhZe0MMLKXQU0z5MJ_6A -# sha256: L5btWgwynKFiMLMmyhK3Rh7I9l4L4-T5l1FvNr-Co0U -# sha256: KP7kQheZHPrZ5qC59-PyYEHiHryWYp6U5YXM0F1J-mU -# sha256: Mm56hUoX-rB2kSBHR2lfj2ktZ0WIo1XEQfsU9mC_Tmg -# sha256: zaWpBIVwnKZ5XIYFbD5f5yZgKLBeU_HVJ_35OmNlprg -# sha256: DLKhR0K1Q_3Wj5MaFM44KRhu0rGyJnoGeHOIyWst2b4 -# sha256: UZH_a5Em0sA53Yf4_wJb7SdLrwf6eK-kb1VrGtcmXW4 -# sha256: gyPgNjey0HLMcEEwC6xuxEjDwolQq0A3YDZ4jpoa9ik -# sha256: hTys2W0fcB3dZ6oD7MBfUYkBNbcmLpInEBEvEqLtKn8 -pyasn1==0.1.9 - -# sha256: iORea7Jd_tJyoe8ucoRh1EtjTCzWiemJtuVqNJxaOuU -# sha256: 8KJgcNbbCIHei8x4RpNLfDyTDY-cedRYg-5ImEvA1nI -pyOpenSSL==0.15.1 - -# sha256: 7qMYNcVuIJavQ2OldFp4SHimHQQ-JH06bWoKMql0H1Y -# sha256: jfvGxFi42rocDzYgqMeACLMjomiye3NZ6SpK5BMl9TU -pyRFC3339==1.0 - -# sha256: Z9WdZs26jWJOA4m4eyqDoXbyHxaodVO1D1cDsj8pusI -python-augeas==0.5.0 - -# sha256: BOk_JJlcQ92Q8zjV2GXKcs4_taU1jU2qSWVXHbNfw-w -# sha256: Pm9ZP-rZj4pSa8PjBpM1MyNuM3KfVS9SiW6lBPVTE_o -python2-pythondialog==3.3.0 - -# sha256: Or5qbT_C-75MYBRCEfRdou2-MYKm9lEa9ru6BZix-ZI -# sha256: k575weEiTZgEBWial__PeCjFbRUXsx1zRkNWwfK3dp4 -# sha256: 6tSu-nAHJJ4F5RsBCVcZ1ajdlXYAifVzCqxWmLGTKRg -# sha256: PMoN8IvQ7ZhDI5BJTOPe0AP15mGqRgvnpzS__jWYNgU -# sha256: Pt5HDT0XujwHY436DRBFK8G25a0yYSemW6d-aq6xG-w -# sha256: aMR5ZPcYbuwwaxNilidyK5B5zURH7Z5eyuzU6shMpzQ -# sha256: 3V05kZUKrkCmyB3hV4lC5z1imAjO_FHRLNFXmA5s_Bg -# sha256: p3xSBiwH63x7MFRdvHPjKZW34Rfup1Axe1y1x6RhjxQ -# sha256: ga-a7EvJYKmgEnxIjxh3La5GNGiSM_BvZUQ-exHr61E -# sha256: 4Hmx2txcBiRswbtv4bI6ULHRFz8u3VEE79QLtzoo9AY -# sha256: -9JnRncsJMuTyLl8va1cueRshrvbG52KdD7gDi-x_F0 -# sha256: mSZu8wo35Dky3uwrfKc-g8jbw7n_cD7HPsprHa5r7-o -# sha256: i2zhyZOQl4O8luC0806iI7_3pN8skL25xODxrJKGieM -pytz==2015.7 - -# sha256: ET-7pVManjSUW302szoIToul0GZLcDyBp8Vy2RkZpbg -# sha256: xXeBXdAPE5QgP8ROuXlySwmPiCZKnviY7kW45enPWH8 -requests==2.9.1 - -# sha256: D_eMQD2bzPWkJabTGhKqa0fxwhyk3CVzp-LzKpczXrE -# sha256: EF-NaGFvgkjiS_DpNy7wTTzBAQTxmA9U1Xss5zpa1Wo -six==1.10.0 - -# sha256: glPOvsSxkJTWfMXtWvmb8duhKFKSIm6Yoxkp-HpdayM -# sha256: BazGegmYDC7P7dNCP3rgEEg57MtV_GRXc-HKoJUcMDA -traceback2==1.4.0 - -# sha256: E_d9CHXbbZtDXh1PQedK1MwutuHVyCSZYJKzQw8Ii7g -# sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk -unittest2==1.1.0 - -# sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo -zope.component==4.2.2 - -# sha256: 3HpZov2Rcw03kxMaXSYbKek-xOKpfxvEh86N7-4v54Y -zope.event==4.1.0 - -# sha256: 8HtjH3pgHNjL0zMtVPQxQscIioMpn4WTVvCNHU1CWbM -# sha256: 3lzKCDuUOdgAL7drvmtJmMWlpyH6sluEKYln8ALfTJQ -# sha256: Z4hBb36n9bipe-lIJTd6ol6L3HNGPge6r5hYsp5zcHc -# sha256: bzIw9yVFGCAeWjcIy7LemMhIME8G497Yv7OeWCXLouE -# sha256: X6V1pSQPBCAMMIhCfQ1Le3N_bpAYgYpR2ND5J6aiUXo -# sha256: UiGUrWpUVzXt11yKg_SNZdGvBk5DKn0yDWT1a6_BLpk -# sha256: 6Mey1AlD9xyZFIyX9myqf1E0FH9XQj-NtbSCUJnOmgk -# sha256: J5Ak8CCGAcPKqQfFOHbjetiGJffq8cs4QtvjYLIocBc -# sha256: LiIanux8zFiImieOoT3P7V75OdgLB4Gamos8scaBSE8 -# sha256: aRGJZUEOyG1E3GuQF-4929WC4MCr7vYrOhnb9sitEys -# sha256: 0E34aG7IZNDK3ozxmff4OuzUFhCaIINNVo-DEN7RLeo -# sha256: 51qUfhXul-fnHgLqMC_rL8YtOiu0Zov5377UOlBqx-c -# sha256: TkXSL7iDIipaufKCoRb-xe4ujRpWjM_2otdbvQ62vPw -# sha256: vOkzm7PHpV4IA7Y9IcWDno5Hm8hcSt9CrkFbcvlPrLI -# sha256: koE4NlJFoOiGmlmZ-8wqRUdaCm7VKklNYNvcVAM1_t0 -# sha256: DYQbobuEDuoOZIncXsr6YSVVSXH1O1rLh3ZEQeYbzro -# sha256: sJyMHUezUxxADgGVaX8UFKYyId5u9HhZik8UYPfZo5I -zope.interface==4.1.3 - -# sha256: uDndLZwRfHAUMMFJlWkYpCOphjtIsJyQ4wpgE-fS9E8 -# sha256: j4MIDaoknQNsvM-4rlzG_wB7iNbZN1ITca-r57Gbrbw -mock==1.0.1 - -# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; -# ADD ALL DEPENDENCIES ABOVE - -# sha256: UMVihR1TbyvQNHzx1CzYiydDitJVGw_mLAGr3-gCGJk -# sha256: ClkIqiGQsLTyyLASRkWYniS9n4CAW6D4GSuBETXFALY -acme==0.4.2 - -# sha256: hbUGND6Eo_q6a97o3o66wwLYJ7koNvwOXh9u5bZNCVI -# sha256: 460kqywseljbDW_Gr_ZU23rWlzNeE-AL4_JwYCRdS-Y -letsencrypt==0.4.2 - -# sha256: KNMAOMrJMr1vLJBDaihGqEmvPbfxgH_dvRk1OFHaM_I -# sha256: SXSg-gIabiV4CBzrfPIyABhfTjKl7YZrKDSVkfE4Vbo -letsencrypt-apache==0.4.2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 UNLIKELY_EOF # ------------------------------------------------------------------------- - cat << "UNLIKELY_EOF" > "$TEMP_DIR/peep.py" + cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" #!/usr/bin/env python -"""peep ("prudently examine every package") verifies that packages conform to a -trusted, locally stored hash and only then installs them:: +"""A small script that can act as a trust root for installing pip 8 - peep install -r requirements.txt - -This makes your deployments verifiably repeatable without having to maintain a -local PyPI mirror or use a vendor lib. Just update the version numbers and -hashes in requirements.txt, and you're all set. +Embed this in your project, and your VCS checkout is all you have to trust. In +a post-peep era, this lets you claw your way to a hash-checking version of pip, +with which you can install the rest of your dependencies safely. All it assumes +is Python 2.6 or better and *some* version of pip already installed. If +anything goes wrong, it will exit with a non-zero status code. """ -# This is here so embedded copies of peep.py are MIT-compliant: -# Copyright (c) 2013 Erik Rose +# This is here so embedded copies are MIT-compliant: +# Copyright (c) 2016 Erik Rose # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -717,957 +681,146 @@ hashes in requirements.txt, and you're all set. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function -try: - xrange = xrange -except NameError: - xrange = range -from base64 import urlsafe_b64encode, urlsafe_b64decode -from binascii import hexlify -import cgi -from collections import defaultdict -from functools import wraps from hashlib import sha256 -from itertools import chain, islice -import mimetypes -from optparse import OptionParser -from os.path import join, basename, splitext, isdir -from pickle import dumps, loads -import re -import sys -from shutil import rmtree, copy -from sys import argv, exit -from tempfile import mkdtemp -import traceback +from os.path import join +from pipes import quote +from shutil import rmtree try: - from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError + from subprocess import check_output +except ImportError: + from subprocess import CalledProcessError, PIPE, Popen + + def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be ' + 'overridden.') + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return output +from sys import exit, version_info +from tempfile import mkdtemp +try: + from urllib2 import build_opener, HTTPHandler, HTTPSHandler except ImportError: from urllib.request import build_opener, HTTPHandler, HTTPSHandler - from urllib.error import HTTPError try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse # 3.4 -# TODO: Probably use six to make urllib stuff work across 2/3. - -from pkg_resources import require, VersionConflict, DistributionNotFound - -# We don't admit our dependency on pip in setup.py, lest a naive user simply -# say `pip install peep.tar.gz` and thus pull down an untrusted copy of pip -# from PyPI. Instead, we make sure it's installed and new enough here and spit -# out an error message if not: -def activate(specifier): - """Make a compatible version of pip importable. Raise a RuntimeError if we - couldn't.""" - try: - for distro in require(specifier): - distro.activate() - except (VersionConflict, DistributionNotFound): - raise RuntimeError('The installed version of pip is too old; peep ' - 'requires ' + specifier) - -# Before 0.6.2, the log module wasn't there, so some -# of our monkeypatching fails. It probably wouldn't be -# much work to support even earlier, though. -activate('pip>=0.6.2') - -import pip -from pip.commands.install import InstallCommand -try: - from pip.download import url_to_path # 1.5.6 -except ImportError: - try: - from pip.util import url_to_path # 0.7.0 - except ImportError: - from pip.util import url_to_filename as url_to_path # 0.6.2 -from pip.exceptions import InstallationError -from pip.index import PackageFinder, Link -try: - from pip.log import logger -except ImportError: - from pip import logger # 6.0 -from pip.req import parse_requirements -try: - from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner -except ImportError: - class NullProgressBar(object): - def __init__(self, *args, **kwargs): - pass - - def iter(self, ret, *args, **kwargs): - return ret - - DownloadProgressBar = DownloadProgressSpinner = NullProgressBar - -__version__ = 3, 1, 1 - -try: - from pip.index import FormatControl # noqa - FORMAT_CONTROL_ARG = 'format_control' - - # The line-numbering bug will be fixed in pip 8. All 7.x releases had it. - PIP_MAJOR_VERSION = int(pip.__version__.split('.')[0]) - PIP_COUNTS_COMMENTS = PIP_MAJOR_VERSION >= 8 -except ImportError: - FORMAT_CONTROL_ARG = 'use_wheel' # pre-7 - PIP_COUNTS_COMMENTS = True +__version__ = 1, 1, 1 -ITS_FINE_ITS_FINE = 0 -SOMETHING_WENT_WRONG = 1 -# "Traditional" for command-line errors according to optparse docs: -COMMAND_LINE_ERROR = 2 -UNHANDLED_EXCEPTION = 3 - -ARCHIVE_EXTENSIONS = ('.tar.bz2', '.tar.gz', '.tgz', '.tar', '.zip') - -MARKER = object() +# wheel has a conditional dependency on argparse: +maybe_argparse = ( + [('https://pypi.python.org/packages/source/a/argparse/' + 'argparse-1.4.0.tar.gz', + '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] + if version_info < (2, 7, 0) else []) -class PipException(Exception): - """When I delegated to pip, it exited with an error.""" - - def __init__(self, error_code): - self.error_code = error_code +PACKAGES = maybe_argparse + [ + # Pip has no dependencies, as it vendors everything: + ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', + '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + # This version of setuptools has only optional dependencies: + ('https://pypi.python.org/packages/source/s/setuptools/' + 'setuptools-20.2.2.tar.gz', + '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), + ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') +] -class UnsupportedRequirementError(Exception): - """An unsupported line was encountered in a requirements file.""" - - -class DownloadError(Exception): - def __init__(self, link, exc): - self.link = link - self.reason = str(exc) - +class HashError(Exception): def __str__(self): - return 'Downloading %s failed: %s' % (self.link, self.reason) + url, path, actual, expected = self.args + return ('{url} did not match the expected hash {expected}. Instead, ' + 'it was {actual}. The file (left at {path}) may have been ' + 'tampered with.'.format(**locals())) -def encoded_hash(sha): - """Return a short, 7-bit-safe representation of a hash. +def hashed_download(url, temp, digest): + """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``, + and return its path.""" + # Based on pip 1.4.1's URLOpener but with cert verification removed. Python + # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert + # authenticity has only privacy (not arbitrary code execution) + # implications, since we're checking hashes. + def opener(): + opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) + return opener - If you pass a sha256, this results in the hash algorithm that the Wheel - format (PEP 427) uses, except here it's intended to be run across the - downloaded archive before unpacking. - - """ - return urlsafe_b64encode(sha.digest()).decode('ascii').rstrip('=') - - -def path_and_line(req): - """Return the path and line number of the file from which an - InstallRequirement came. - - """ - path, line = (re.match(r'-r (.*) \(line (\d+)\)$', - req.comes_from).groups()) - return path, int(line) - - -def hashes_above(path, line_number): - """Yield hashes from contiguous comment lines before line ``line_number``. - - """ - def hash_lists(path): - """Yield lists of hashes appearing between non-comment lines. - - The lists will be in order of appearance and, for each non-empty - list, their place in the results will coincide with that of the - line number of the corresponding result from `parse_requirements` - (which changed in pip 7.0 to not count comments). - - """ - hashes = [] - with open(path) as file: - for lineno, line in enumerate(file, 1): - match = HASH_COMMENT_RE.match(line) - if match: # Accumulate this hash. - hashes.append(match.groupdict()['hash']) - if not IGNORED_LINE_RE.match(line): - yield hashes # Report hashes seen so far. - hashes = [] - elif PIP_COUNTS_COMMENTS: - # Comment: count as normal req but have no hashes. - yield [] - - return next(islice(hash_lists(path), line_number - 1, None)) - - -def run_pip(initial_args): - """Delegate to pip the given args (starting with the subcommand), and raise - ``PipException`` if something goes wrong.""" - status_code = pip.main(initial_args) - - # Clear out the registrations in the pip "logger" singleton. Otherwise, - # loggers keep getting appended to it with every run. Pip assumes only one - # command invocation will happen per interpreter lifetime. - logger.consumers = [] - - if status_code: - raise PipException(status_code) - - -def hash_of_file(path): - """Return the hash of a downloaded file.""" - with open(path, 'rb') as archive: - sha = sha256() + def read_chunks(response, chunk_size): while True: - data = archive.read(2 ** 20) - if not data: + chunk = response.read(chunk_size) + if not chunk: break - sha.update(data) - return encoded_hash(sha) - - -def is_git_sha(text): - """Return whether this is probably a git sha""" - # Handle both the full sha as well as the 7-character abbreviation - if len(text) in (40, 7): - try: - int(text, 16) - return True - except ValueError: - pass - return False - - -def filename_from_url(url): - parsed = urlparse(url) - path = parsed.path - return path.split('/')[-1] - - -def requirement_args(argv, want_paths=False, want_other=False): - """Return an iterable of filtered arguments. - - :arg argv: Arguments, starting after the subcommand - :arg want_paths: If True, the returned iterable includes the paths to any - requirements files following a ``-r`` or ``--requirement`` option. - :arg want_other: If True, the returned iterable includes the args that are - not a requirement-file path or a ``-r`` or ``--requirement`` flag. - - """ - was_r = False - for arg in argv: - # Allow for requirements files named "-r", don't freak out if there's a - # trailing "-r", etc. - if was_r: - if want_paths: - yield arg - was_r = False - elif arg in ['-r', '--requirement']: - was_r = True - else: - if want_other: - yield arg - -# any line that is a comment or just whitespace -IGNORED_LINE_RE = re.compile(r'^(\s*#.*)?\s*$') - -HASH_COMMENT_RE = re.compile( - r""" - \s*\#\s+ # Lines that start with a '#' - (?Psha256):\s+ # Hash type is hardcoded to be sha256 for now. - (?P[^\s]+) # Hashes can be anything except '#' or spaces. - \s* # Suck up whitespace before the comment or - # just trailing whitespace if there is no - # comment. Also strip trailing newlines. - (?:\#(?P.*))? # Comments can be anything after a whitespace+# - # and are optional. - $""", re.X) - - -def peep_hash(argv): - """Return the peep hash of one or more files, returning a shell status code - or raising a PipException. - - :arg argv: The commandline args, starting after the subcommand - - """ - parser = OptionParser( - usage='usage: %prog hash file [file ...]', - description='Print a peep hash line for one or more files: for ' - 'example, "# sha256: ' - 'oz42dZy6Gowxw8AelDtO4gRgTW_xPdooH484k7I5EOY".') - _, paths = parser.parse_args(args=argv) - if paths: - for path in paths: - print('# sha256:', hash_of_file(path)) - return ITS_FINE_ITS_FINE - else: - parser.print_usage() - return COMMAND_LINE_ERROR - - -class EmptyOptions(object): - """Fake optparse options for compatibility with pip<1.2 - - pip<1.2 had a bug in parse_requirements() in which the ``options`` kwarg - was required. We work around that by passing it a mock object. - - """ - default_vcs = None - skip_requirements_regex = None - isolated_mode = False - - -def memoize(func): - """Memoize a method that should return the same result every time on a - given instance. - - """ - @wraps(func) - def memoizer(self): - if not hasattr(self, '_cache'): - self._cache = {} - if func.__name__ not in self._cache: - self._cache[func.__name__] = func(self) - return self._cache[func.__name__] - return memoizer - - -def package_finder(argv): - """Return a PackageFinder respecting command-line options. - - :arg argv: Everything after the subcommand - - """ - # We instantiate an InstallCommand and then use some of its private - # machinery--its arg parser--for our own purposes, like a virus. This - # approach is portable across many pip versions, where more fine-grained - # ones are not. Ignoring options that don't exist on the parser (for - # instance, --use-wheel) gives us a straightforward method of backward - # compatibility. - try: - command = InstallCommand() - except TypeError: - # This is likely pip 1.3.0's "__init__() takes exactly 2 arguments (1 - # given)" error. In that version, InstallCommand takes a top=level - # parser passed in from outside. - from pip.baseparser import create_main_parser - command = InstallCommand(create_main_parser()) - # The downside is that it essentially ruins the InstallCommand class for - # further use. Calling out to pip.main() within the same interpreter, for - # example, would result in arguments parsed this time turning up there. - # Thus, we deepcopy the arg parser so we don't trash its singletons. Of - # course, deepcopy doesn't work on these objects, because they contain - # uncopyable regex patterns, so we pickle and unpickle instead. Fun! - options, _ = loads(dumps(command.parser)).parse_args(argv) - - # Carry over PackageFinder kwargs that have [about] the same names as - # options attr names: - possible_options = [ - 'find_links', - FORMAT_CONTROL_ARG, - ('allow_all_prereleases', 'pre'), - 'process_dependency_links' - ] - kwargs = {} - for option in possible_options: - kw, attr = option if isinstance(option, tuple) else (option, option) - value = getattr(options, attr, MARKER) - if value is not MARKER: - kwargs[kw] = value - - # Figure out index_urls: - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index: - index_urls = [] - index_urls += getattr(options, 'mirrors', []) - - # If pip is new enough to have a PipSession, initialize one, since - # PackageFinder requires it: - if hasattr(command, '_build_session'): - kwargs['session'] = command._build_session(options) - - return PackageFinder(index_urls=index_urls, **kwargs) - - -class DownloadedReq(object): - """A wrapper around InstallRequirement which offers additional information - based on downloading and examining a corresponding package archive - - These are conceptually immutable, so we can get away with memoizing - expensive things. - - """ - def __init__(self, req, argv, finder): - """Download a requirement, compare its hashes, and return a subclass - of DownloadedReq depending on its state. - - :arg req: The InstallRequirement I am based on - :arg argv: The args, starting after the subcommand - - """ - self._req = req - self._argv = argv - self._finder = finder - - # We use a separate temp dir for each requirement so requirements - # (from different indices) that happen to have the same archive names - # don't overwrite each other, leading to a security hole in which the - # latter is a hash mismatch, the former has already passed the - # comparison, and the latter gets installed. - self._temp_path = mkdtemp(prefix='peep-') - # Think of DownloadedReq as a one-shot state machine. It's an abstract - # class that ratchets forward to being one of its own subclasses, - # depending on its package status. Then it doesn't move again. - self.__class__ = self._class() - - def dispose(self): - """Delete temp files and dirs I've made. Render myself useless. - - Do not call further methods on me after calling dispose(). - - """ - rmtree(self._temp_path) - - def _version(self): - """Deduce the version number of the downloaded package from its filename.""" - # TODO: Can we delete this method and just print the line from the - # reqs file verbatim instead? - def version_of_archive(filename, package_name): - # Since we know the project_name, we can strip that off the left, strip - # any archive extensions off the right, and take the rest as the - # version. - for ext in ARCHIVE_EXTENSIONS: - if filename.endswith(ext): - filename = filename[:-len(ext)] - break - # Handle github sha tarball downloads. - if is_git_sha(filename): - filename = package_name + '-' + filename - if not filename.lower().replace('_', '-').startswith(package_name.lower()): - # TODO: Should we replace runs of [^a-zA-Z0-9.], not just _, with -? - give_up(filename, package_name) - return filename[len(package_name) + 1:] # Strip off '-' before version. - - def version_of_wheel(filename, package_name): - # For Wheel files (http://legacy.python.org/dev/peps/pep-0427/#file- - # name-convention) we know the format bits are '-' separated. - whl_package_name, version, _rest = filename.split('-', 2) - # Do the alteration to package_name from PEP 427: - our_package_name = re.sub(r'[^\w\d.]+', '_', package_name, re.UNICODE) - if whl_package_name != our_package_name: - give_up(filename, whl_package_name) - return version - - def give_up(filename, package_name): - raise RuntimeError("The archive '%s' didn't start with the package name " - "'%s', so I couldn't figure out the version number. " - "My bad; improve me." % - (filename, package_name)) - - get_version = (version_of_wheel - if self._downloaded_filename().endswith('.whl') - else version_of_archive) - return get_version(self._downloaded_filename(), self._project_name()) - - def _is_always_unsatisfied(self): - """Returns whether this requirement is always unsatisfied - - This would happen in cases where we can't determine the version - from the filename. - - """ - # If this is a github sha tarball, then it is always unsatisfied - # because the url has a commit sha in it and not the version - # number. - url = self._url() - if url: - filename = filename_from_url(url) - if filename.endswith(ARCHIVE_EXTENSIONS): - filename, ext = splitext(filename) - if is_git_sha(filename): - return True - return False - - @memoize # Avoid hitting the file[cache] over and over. - def _expected_hashes(self): - """Return a list of known-good hashes for this package.""" - return hashes_above(*path_and_line(self._req)) - - def _download(self, link): - """Download a file, and return its name within my temp dir. - - This does no verification of HTTPS certs, but our checking hashes - makes that largely unimportant. It would be nice to be able to use the - requests lib, which can verify certs, but it is guaranteed to be - available only in pip >= 1.5. - - This also drops support for proxies and basic auth, though those could - be added back in. - - """ - # Based on pip 1.4.1's URLOpener but with cert verification removed - def opener(is_https): - if is_https: - opener = build_opener(HTTPSHandler()) - # Strip out HTTPHandler to prevent MITM spoof: - for handler in opener.handlers: - if isinstance(handler, HTTPHandler): - opener.handlers.remove(handler) - else: - opener = build_opener() - return opener - - # Descended from unpack_http_url() in pip 1.4.1 - def best_filename(link, response): - """Return the most informative possible filename for a download, - ideally with a proper extension. - - """ - content_type = response.info().get('content-type', '') - filename = link.filename # fallback - # Have a look at the Content-Disposition header for a better guess: - content_disposition = response.info().get('content-disposition') - if content_disposition: - type, params = cgi.parse_header(content_disposition) - # We use ``or`` here because we don't want to use an "empty" value - # from the filename param: - filename = params.get('filename') or filename - ext = splitext(filename)[1] - if not ext: - ext = mimetypes.guess_extension(content_type) - if ext: - filename += ext - if not ext and link.url != response.geturl(): - ext = splitext(response.geturl())[1] - if ext: - filename += ext - return filename - - # Descended from _download_url() in pip 1.4.1 - def pipe_to_file(response, path, size=0): - """Pull the data off an HTTP response, shove it in a new file, and - show progress. - - :arg response: A file-like object to read from - :arg path: The path of the new file - :arg size: The expected size, in bytes, of the download. 0 for - unknown or to suppress progress indication (as for cached - downloads) - - """ - def response_chunks(chunk_size): - while True: - chunk = response.read(chunk_size) - if not chunk: - break - yield chunk - - print('Downloading %s%s...' % ( - self._req.req, - (' (%sK)' % (size / 1000)) if size > 1000 else '')) - progress_indicator = (DownloadProgressBar(max=size).iter if size - else DownloadProgressSpinner().iter) - with open(path, 'wb') as file: - for chunk in progress_indicator(response_chunks(4096), 4096): - file.write(chunk) - - url = link.url.split('#', 1)[0] - try: - response = opener(urlparse(url).scheme != 'http').open(url) - except (HTTPError, IOError) as exc: - raise DownloadError(link, exc) - filename = best_filename(link, response) - try: - size = int(response.headers['content-length']) - except (ValueError, KeyError, TypeError): - size = 0 - pipe_to_file(response, join(self._temp_path, filename), size=size) - return filename - - # Based on req_set.prepare_files() in pip bb2a8428d4aebc8d313d05d590f386fa3f0bbd0f - @memoize # Avoid re-downloading. - def _downloaded_filename(self): - """Download the package's archive if necessary, and return its - filename. - - --no-deps is implied, as we have reimplemented the bits that would - ordinarily do dependency resolution. - - """ - # Peep doesn't support requirements that don't come down as a single - # file, because it can't hash them. Thus, it doesn't support editable - # requirements, because pip itself doesn't support editable - # requirements except for "local projects or a VCS url". Nor does it - # support VCS requirements yet, because we haven't yet come up with a - # portable, deterministic way to hash them. In summary, all we support - # is == requirements and tarballs/zips/etc. - - # TODO: Stop on reqs that are editable or aren't ==. - - # If the requirement isn't already specified as a URL, get a URL - # from an index: - link = self._link() or self._finder.find_requirement(self._req, upgrade=False) - - if link: - lower_scheme = link.scheme.lower() # pip lower()s it for some reason. - if lower_scheme == 'http' or lower_scheme == 'https': - file_path = self._download(link) - return basename(file_path) - elif lower_scheme == 'file': - # The following is inspired by pip's unpack_file_url(): - link_path = url_to_path(link.url_without_fragment) - if isdir(link_path): - raise UnsupportedRequirementError( - "%s: %s is a directory. So that it can compute " - "a hash, peep supports only filesystem paths which " - "point to files" % - (self._req, link.url_without_fragment)) - else: - copy(link_path, self._temp_path) - return basename(link_path) - else: - raise UnsupportedRequirementError( - "%s: The download link, %s, would not result in a file " - "that can be hashed. Peep supports only == requirements, " - "file:// URLs pointing to files (not folders), and " - "http:// and https:// URLs pointing to tarballs, zips, " - "etc." % (self._req, link.url)) - else: - raise UnsupportedRequirementError( - "%s: couldn't determine where to download this requirement from." - % (self._req,)) - - def install(self): - """Install the package I represent, without dependencies. - - Obey typical pip-install options passed in on the command line. - - """ - other_args = list(requirement_args(self._argv, want_other=True)) - archive_path = join(self._temp_path, self._downloaded_filename()) - # -U so it installs whether pip deems the requirement "satisfied" or - # not. This is necessary for GitHub-sourced zips, which change without - # their version numbers changing. - run_pip(['install'] + other_args + ['--no-deps', '-U', archive_path]) - - @memoize - def _actual_hash(self): - """Download the package's archive if necessary, and return its hash.""" - return hash_of_file(join(self._temp_path, self._downloaded_filename())) - - def _project_name(self): - """Return the inner Requirement's "unsafe name". - - Raise ValueError if there is no name. - - """ - name = getattr(self._req.req, 'project_name', '') - if name: - return name - raise ValueError('Requirement has no project_name.') - - def _name(self): - return self._req.name - - def _link(self): - try: - return self._req.link - except AttributeError: - # The link attribute isn't available prior to pip 6.1.0, so fall - # back to the now deprecated 'url' attribute. - return Link(self._req.url) if self._req.url else None - - def _url(self): - link = self._link() - return link.url if link else None - - @memoize # Avoid re-running expensive check_if_exists(). - def _is_satisfied(self): - self._req.check_if_exists() - return (self._req.satisfied_by and - not self._is_always_unsatisfied()) - - def _class(self): - """Return the class I should be, spanning a continuum of goodness.""" - try: - self._project_name() - except ValueError: - return MalformedReq - if self._is_satisfied(): - return SatisfiedReq - if not self._expected_hashes(): - return MissingReq - if self._actual_hash() not in self._expected_hashes(): - return MismatchedReq - return InstallableReq - - @classmethod - def foot(cls): - """Return the text to be printed once, after all of the errors from - classes of my type are printed. - - """ - return '' - - -class MalformedReq(DownloadedReq): - """A requirement whose package name could not be determined""" - - @classmethod - def head(cls): - return 'The following requirements could not be processed:\n' - - def error(self): - return '* Unable to determine package name from URL %s; add #egg=' % self._url() - - -class MissingReq(DownloadedReq): - """A requirement for which no hashes were specified in the requirements file""" - - @classmethod - def head(cls): - return ('The following packages had no hashes specified in the requirements file, which\n' - 'leaves them open to tampering. Vet these packages to your satisfaction, then\n' - 'add these "sha256" lines like so:\n\n') - - def error(self): - if self._url(): - # _url() always contains an #egg= part, or this would be a - # MalformedRequest. - line = self._url() - else: - line = '%s==%s' % (self._name(), self._version()) - return '# sha256: %s\n%s\n' % (self._actual_hash(), line) - - -class MismatchedReq(DownloadedReq): - """A requirement for which the downloaded file didn't match any of my hashes.""" - @classmethod - def head(cls): - return ("THE FOLLOWING PACKAGES DIDN'T MATCH THE HASHES SPECIFIED IN THE REQUIREMENTS\n" - "FILE. If you have updated the package versions, update the hashes. If not,\n" - "freak out, because someone has tampered with the packages.\n\n") - - def error(self): - preamble = ' %s: expected' % self._project_name() - if len(self._expected_hashes()) > 1: - preamble += ' one of' - padding = '\n' + ' ' * (len(preamble) + 1) - return '%s %s\n%s got %s' % (preamble, - padding.join(self._expected_hashes()), - ' ' * (len(preamble) - 4), - self._actual_hash()) - - @classmethod - def foot(cls): - return '\n' - - -class SatisfiedReq(DownloadedReq): - """A requirement which turned out to be already installed""" - - @classmethod - def head(cls): - return ("These packages were already installed, so we didn't need to download or build\n" - "them again. If you installed them with peep in the first place, you should be\n" - "safe. If not, uninstall them, then re-attempt your install with peep.\n") - - def error(self): - return ' %s' % (self._req,) - - -class InstallableReq(DownloadedReq): - """A requirement whose hash matched and can be safely installed""" - - -# DownloadedReq subclasses that indicate an error that should keep us from -# going forward with installation, in the order in which their errors should -# be reported: -ERROR_CLASSES = [MismatchedReq, MissingReq, MalformedReq] - - -def bucket(things, key): - """Return a map of key -> list of things.""" - ret = defaultdict(list) - for thing in things: - ret[key(thing)].append(thing) - return ret - - -def first_every_last(iterable, first, every, last): - """Execute something before the first item of iter, something else for each - item, and a third thing after the last. - - If there are no items in the iterable, don't execute anything. - - """ - did_first = False - for item in iterable: - if not did_first: - did_first = True - first(item) - every(item) - if did_first: - last(item) - - -def _parse_requirements(path, finder): - try: - # list() so the generator that is parse_requirements() actually runs - # far enough to report a TypeError - return list(parse_requirements( - path, options=EmptyOptions(), finder=finder)) - except TypeError: - # session is a required kwarg as of pip 6.0 and will raise - # a TypeError if missing. It needs to be a PipSession instance, - # but in older versions we can't import it from pip.download - # (nor do we need it at all) so we only import it in this except block - from pip.download import PipSession - return list(parse_requirements( - path, options=EmptyOptions(), session=PipSession(), finder=finder)) - - -def downloaded_reqs_from_path(path, argv): - """Return a list of DownloadedReqs representing the requirements parsed - out of a given requirements file. - - :arg path: The path to the requirements file - :arg argv: The commandline args, starting after the subcommand - - """ - finder = package_finder(argv) - return [DownloadedReq(req, argv, finder) for req in - _parse_requirements(path, finder)] - - -def peep_install(argv): - """Perform the ``peep install`` subcommand, returning a shell status code - or raising a PipException. - - :arg argv: The commandline args, starting after the subcommand - - """ - output = [] - out = output.append - reqs = [] - try: - req_paths = list(requirement_args(argv, want_paths=True)) - if not req_paths: - out("You have to specify one or more requirements files with the -r option, because\n" - "otherwise there's nowhere for peep to look up the hashes.\n") - return COMMAND_LINE_ERROR - - # We're a "peep install" command, and we have some requirement paths. - reqs = list(chain.from_iterable( - downloaded_reqs_from_path(path, argv) - for path in req_paths)) - buckets = bucket(reqs, lambda r: r.__class__) - - # Skip a line after pip's "Cleaning up..." so the important stuff - # stands out: - if any(buckets[b] for b in ERROR_CLASSES): - out('\n') - - printers = (lambda r: out(r.head()), - lambda r: out(r.error() + '\n'), - lambda r: out(r.foot())) - for c in ERROR_CLASSES: - first_every_last(buckets[c], *printers) - - if any(buckets[b] for b in ERROR_CLASSES): - out('-------------------------------\n' - 'Not proceeding to installation.\n') - return SOMETHING_WENT_WRONG - else: - for req in buckets[InstallableReq]: - req.install() - - first_every_last(buckets[SatisfiedReq], *printers) - - return ITS_FINE_ITS_FINE - except (UnsupportedRequirementError, InstallationError, DownloadError) as exc: - out(str(exc)) - return SOMETHING_WENT_WRONG - finally: - for req in reqs: - req.dispose() - print(''.join(output)) - - -def peep_port(paths): - """Convert a peep requirements file to one compatble with pip-8 hashing. - - Loses comments and tromps on URLs, so the result will need a little manual - massaging, but the hard part--the hash conversion--is done for you. - - """ - if not paths: - print('Please specify one or more requirements files so I have ' - 'something to port.\n') - return COMMAND_LINE_ERROR - - comes_from = None - for req in chain.from_iterable( - _parse_requirements(path, package_finder(argv)) for path in paths): - req_path, req_line = path_and_line(req) - hashes = [hexlify(urlsafe_b64decode((hash + '=').encode('ascii'))).decode('ascii') - for hash in hashes_above(req_path, req_line)] - if req_path != comes_from: - print() - print('# from %s' % req_path) - print() - comes_from = req_path - - if not hashes: - print(req.req) - else: - print('%s' % (req.link if getattr(req, 'link', None) else req.req), end='') - for hash in hashes: - print(' \\') - print(' --hash=sha256:%s' % hash, end='') - print() + yield chunk + + response = opener().open(url) + path = join(temp, urlparse(url).path.split('/')[-1]) + actual_hash = sha256() + with open(path, 'wb') as file: + for chunk in read_chunks(response, 4096): + file.write(chunk) + actual_hash.update(chunk) + + actual_digest = actual_hash.hexdigest() + if actual_digest != digest: + raise HashError(url, path, actual_digest, digest) + return path def main(): - """Be the top-level entrypoint. Return a shell status code.""" - commands = {'hash': peep_hash, - 'install': peep_install, - 'port': peep_port} + temp = mkdtemp(prefix='pipstrap-') try: - if len(argv) >= 2 and argv[1] in commands: - return commands[argv[1]](argv[2:]) - else: - # Fall through to top-level pip main() for everything else: - return pip.main() - except PipException as exc: - return exc.error_code - - -def exception_handler(exc_type, exc_value, exc_tb): - print('Oh no! Peep had a problem while trying to do stuff. Please write up a bug report') - print('with the specifics so we can fix it:') - print() - print('https://github.com/erikrose/peep/issues/new') - print() - print('Here are some particulars you can copy and paste into the bug report:') - print() - print('---') - print('peep:', repr(__version__)) - print('python:', repr(sys.version)) - print('pip:', repr(getattr(pip, '__version__', 'no __version__ attr'))) - print('Command line: ', repr(sys.argv)) - print( - ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))) - print('---') + downloads = [hashed_download(url, temp, digest) + for url, digest in PACKAGES] + check_output('pip install --no-index --no-deps -U ' + + ' '.join(quote(d) for d in downloads), + shell=True) + except HashError as exc: + print(exc) + except Exception: + rmtree(temp) + raise + else: + rmtree(temp) + return 0 + return 1 if __name__ == '__main__': - try: - exit(main()) - except Exception: - exception_handler(*sys.exc_info()) - exit(UNHANDLED_EXCEPTION) + exit(main()) UNLIKELY_EOF # ------------------------------------------------------------------------- - InstallRequirements "setuptools-requirements.txt" - InstallRequirements "letsencrypt-auto-requirements.txt" + # Set PATH so pipstrap upgrades the right (v)env: + PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" + set +e + PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_STATUS=$? + set -e + rm -rf "$TEMP_DIR" + if [ "$PIP_STATUS" != 0 ]; then + # Report error. (Otherwise, be quiet.) + echo "Had a problem while installing Python packages:" + echo "$PIP_OUT" + rm -rf "$VENV_PATH" + exit 1 + fi echo "Installation succeeded." fi echo "Requesting root privileges to run letsencrypt..." diff --git a/letsencrypt-auto-source/Dockerfile b/letsencrypt-auto-source/Dockerfile index ad2465fda..23e8f26de 100644 --- a/letsencrypt-auto-source/Dockerfile +++ b/letsencrypt-auto-source/Dockerfile @@ -17,16 +17,16 @@ RUN apt-get update && \ apt-get clean RUN pip install nose -RUN mkdir -p /home/lea/letsencrypt +RUN mkdir -p /home/lea/certbot # Install fake testing CA: COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/ RUN update-ca-certificates # Copy code: -COPY . /home/lea/letsencrypt/letsencrypt-auto-source +COPY . /home/lea/certbot/letsencrypt-auto-source USER lea WORKDIR /home/lea -CMD ["nosetests", "-v", "-s", "letsencrypt/letsencrypt-auto-source/tests"] +CMD ["nosetests", "-v", "-s", "certbot/letsencrypt-auto-source/tests"] diff --git a/letsencrypt-auto-source/build.py b/letsencrypt-auto-source/build.py index 9a5fc46a7..ea74f9766 100755 --- a/letsencrypt-auto-source/build.py +++ b/letsencrypt-auto-source/build.py @@ -14,11 +14,11 @@ from sys import argv DIR = dirname(abspath(__file__)) -def le_version(build_script_dir): - """Return the version number stamped in letsencrypt/__init__.py.""" +def certbot_version(build_script_dir): + """Return the version number stamped in certbot/__init__.py.""" return re.search('''^__version__ = ['"](.+)['"].*''', file_contents(join(dirname(build_script_dir), - 'letsencrypt', + 'certbot', '__init__.py')), re.M).group(1) @@ -32,13 +32,13 @@ def build(version=None, requirements=None): """Return the built contents of the letsencrypt-auto script. :arg version: The version to attach to the script. Default: the version of - the letsencrypt package + the certbot package :arg requirements: The contents of the requirements file to embed. Default: contents of letsencrypt-auto-requirements.txt """ special_replacements = { - 'LE_AUTO_VERSION': version or le_version(DIR) + 'LE_AUTO_VERSION': version or certbot_version(DIR) } if requirements: special_replacements['letsencrypt-auto-requirements.txt'] = requirements diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 0590e5d43..111f2b272 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ 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" -LE_AUTO_VERSION="0.5.0.dev0" +LE_AUTO_VERSION="0.6.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 @@ -348,28 +348,45 @@ BootstrapFreeBsd() { } BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } @@ -428,7 +445,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -627,15 +645,15 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.4.2 \ - --hash=sha256:50c562851d536f2bd0347cf1d42cd88b27438ad2551b0fe62c01abdfe8021899 \ - --hash=sha256:0a5908aa2190b0b4f2c8b0124645989e24bd9f80805ba0f8192b811135c500b6 -letsencrypt==0.4.2 \ - --hash=sha256:85b506343e84a3faba6bdee8de8ebac302d827b92836fc0e5e1f6ee5b64d0952 \ - --hash=sha256:e3ad24ab2c2c7a58db0d6fc6aff654db7ad697335e13e00be3f27060245d4be6 -letsencrypt-apache==0.4.2 \ - --hash=sha256:4974a0fa021a6e2578081ceb7cf23200185f4e32a5ed866b28349591f13855ba \ - --hash=sha256:28d30038cac932bd6f2c90436a2846a849af3db7f1807fddbd19353851da33f2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -683,7 +701,7 @@ except ImportError: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] - raise CalledProcessError(retcode, cmd, output=output) + raise CalledProcessError(retcode, cmd) return output from sys import exit, version_info from tempfile import mkdtemp @@ -697,7 +715,7 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 0 +__version__ = 1, 1, 1 # wheel has a conditional dependency on argparse: diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 9e2e610c0..36ab206aa 100644 Binary files a/letsencrypt-auto-source/letsencrypt-auto.sig and b/letsencrypt-auto-source/letsencrypt-auto.sig differ diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 40edca7fe..2c8e1ec4c 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -46,7 +46,7 @@ for arg in "$@" ; do done # letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` @@ -157,7 +157,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -176,7 +176,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -218,7 +219,7 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." + echo "Requesting root privileges to run certbot..." echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else @@ -226,8 +227,8 @@ else # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # letsencrypt-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then # If it looks like we've never bootstrapped before, bootstrap: diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index bbafb39d7..57ed11399 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -69,7 +69,7 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 4bdf34116..79e58eb3f 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -1,26 +1,43 @@ BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 1e76417b7..27cfb3d43 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -1,5 +1,5 @@ # This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ @@ -178,12 +178,12 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.4.2 \ - --hash=sha256:50c562851d536f2bd0347cf1d42cd88b27438ad2551b0fe62c01abdfe8021899 \ - --hash=sha256:0a5908aa2190b0b4f2c8b0124645989e24bd9f80805ba0f8192b811135c500b6 -letsencrypt==0.4.2 \ - --hash=sha256:85b506343e84a3faba6bdee8de8ebac302d827b92836fc0e5e1f6ee5b64d0952 \ - --hash=sha256:e3ad24ab2c2c7a58db0d6fc6aff654db7ad697335e13e00be3f27060245d4be6 -letsencrypt-apache==0.4.2 \ - --hash=sha256:4974a0fa021a6e2578081ceb7cf23200185f4e32a5ed866b28349591f13855ba \ - --hash=sha256:28d30038cac932bd6f2c90436a2846a849af3db7f1807fddbd19353851da33f2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 diff --git a/letsencrypt-auto-source/pieces/pipstrap.py b/letsencrypt-auto-source/pieces/pipstrap.py index 016f7ca13..505f8ca72 100755 --- a/letsencrypt-auto-source/pieces/pipstrap.py +++ b/letsencrypt-auto-source/pieces/pipstrap.py @@ -41,7 +41,7 @@ except ImportError: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] - raise CalledProcessError(retcode, cmd, output=output) + raise CalledProcessError(retcode, cmd) return output from sys import exit, version_info from tempfile import mkdtemp @@ -55,7 +55,7 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 0 +__version__ = 1, 1, 1 # wheel has a conditional dependency on argparse: diff --git a/letsencrypt-compatibility-test/MANIFEST.in b/letsencrypt-compatibility-test/MANIFEST.in deleted file mode 100644 index 24d777841..000000000 --- a/letsencrypt-compatibility-test/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include LICENSE.txt -include README.rst -recursive-include docs * -include letsencrypt_compatibility_test/configurators/apache/a2enmod.sh -include letsencrypt_compatibility_test/configurators/apache/a2dismod.sh -include letsencrypt_compatibility_test/configurators/apache/Dockerfile -recursive-include letsencrypt_compatibility_test/testdata * diff --git a/letsencrypt-compatibility-test/README.rst b/letsencrypt-compatibility-test/README.rst deleted file mode 100644 index 4afd999a8..000000000 --- a/letsencrypt-compatibility-test/README.rst +++ /dev/null @@ -1 +0,0 @@ -Compatibility tests for Let's Encrypt client diff --git a/letsencrypt-compatibility-test/docs/api/index.rst b/letsencrypt-compatibility-test/docs/api/index.rst deleted file mode 100644 index f792a2cc3..000000000 --- a/letsencrypt-compatibility-test/docs/api/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -:mod:`letsencrypt_compatibility_test` -------------------------------------- - -.. automodule:: letsencrypt_compatibility_test - :members: - -:mod:`letsencrypt_compatibility_test.errors` -============================================ - -.. automodule:: letsencrypt_compatibility_test.errors - :members: - -:mod:`letsencrypt_compatibility_test.interfaces` -================================================ - -.. automodule:: letsencrypt_compatibility_test.interfaces - :members: - -:mod:`letsencrypt_compatibility_test.test_driver` -================================================= - -.. automodule:: letsencrypt_compatibility_test.test_driver - :members: - -:mod:`letsencrypt_compatibility_test.util` -========================================== - -.. automodule:: letsencrypt_compatibility_test.util - :members: - -:mod:`letsencrypt_compatibility_test.configurators` -=================================================== - -.. automodule:: letsencrypt_compatibility_test.configurators - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: letsencrypt_compatibility_test.configurators.apache - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache.apache24` -------------------------------------------------------------------- - -.. automodule:: letsencrypt_compatibility_test.configurators.apache.apache24 - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache.common` -------------------------------------------------------------------- - -.. automodule:: letsencrypt_compatibility_test.configurators.apache.common - :members: diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/__init__.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/__init__.py deleted file mode 100644 index 90807863a..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt compatibility test""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/__init__.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/__init__.py deleted file mode 100644 index bf7b3471f..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt compatibility test configurators""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile deleted file mode 100644 index 392f5efa6..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM httpd -MAINTAINER Brad Warren - -RUN mkdir /var/run/apache2 - -ENV APACHE_RUN_USER=daemon \ - APACHE_RUN_GROUP=daemon \ - APACHE_PID_FILE=/usr/local/apache2/logs/httpd.pid \ - APACHE_RUN_DIR=/var/run/apache2 \ - APACHE_LOCK_DIR=/var/lock \ - APACHE_LOG_DIR=/usr/local/apache2/logs - -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh /usr/local/bin/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh /usr/local/bin/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem /usr/local/apache2/conf/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem /usr/local/apache2/conf/ - -# Note: this only exposes the port to other docker containers. You -# still have to bind to 443@host at runtime. -EXPOSE 443 diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/__init__.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/__init__.py deleted file mode 100644 index 9feca23d4..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt compatibility test Apache configurators""" diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/errors.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/errors.py deleted file mode 100644 index 3b7eb6911..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Let's Encrypt compatibility test errors""" - - -class Error(Exception): - """Generic Let's Encrypt compatibility test error""" diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in index 912d624d9..97e2ad3df 100644 --- a/letsencrypt-nginx/MANIFEST.in +++ b/letsencrypt-nginx/MANIFEST.in @@ -1,5 +1,2 @@ include LICENSE.txt include README.rst -recursive-include docs * -recursive-include letsencrypt_nginx/tests/testdata * -include letsencrypt_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/README.rst b/letsencrypt-nginx/README.rst index ff6d50ce4..cd1f32fb8 100644 --- a/letsencrypt-nginx/README.rst +++ b/letsencrypt-nginx/README.rst @@ -1 +1,2 @@ -Nginx plugin for Let's Encrypt client +This package is a simple shim for backwards compatibility around +``certbot-nginx``, the Nginx plugin for ``certbot``. diff --git a/letsencrypt-nginx/docs/api/nginxparser.rst b/letsencrypt-nginx/docs/api/nginxparser.rst deleted file mode 100644 index e55bda0b1..000000000 --- a/letsencrypt-nginx/docs/api/nginxparser.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.nginxparser` ------------------------------------- - -.. automodule:: letsencrypt_nginx.nginxparser - :members: diff --git a/letsencrypt-nginx/docs/api/obj.rst b/letsencrypt-nginx/docs/api/obj.rst deleted file mode 100644 index 418b87cf7..000000000 --- a/letsencrypt-nginx/docs/api/obj.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.obj` ----------------------------- - -.. automodule:: letsencrypt_nginx.obj - :members: diff --git a/letsencrypt-nginx/docs/api/parser.rst b/letsencrypt-nginx/docs/api/parser.rst deleted file mode 100644 index 6582263ef..000000000 --- a/letsencrypt-nginx/docs/api/parser.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.parser` -------------------------------- - -.. automodule:: letsencrypt_nginx.parser - :members: diff --git a/letsencrypt-nginx/docs/api/tls_sni_01.rst b/letsencrypt-nginx/docs/api/tls_sni_01.rst deleted file mode 100644 index f9f584b0c..000000000 --- a/letsencrypt-nginx/docs/api/tls_sni_01.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.tls_sni_01` ------------------------------------ - -.. automodule:: letsencrypt_nginx.tls_sni_01 - :members: diff --git a/letsencrypt-nginx/letsencrypt_nginx/__init__.py b/letsencrypt-nginx/letsencrypt_nginx/__init__.py index 34db9673d..aa14fe963 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/__init__.py +++ b/letsencrypt-nginx/letsencrypt_nginx/__init__.py @@ -1 +1,8 @@ -"""Let's Encrypt nginx plugin.""" +"""Let's Encrypt Nginx plugin.""" +import sys + + +import certbot_nginx + + +sys.modules['letsencrypt_nginx'] = certbot_nginx diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/__init__.py b/letsencrypt-nginx/letsencrypt_nginx/tests/__init__.py deleted file mode 100644 index 157a70759..000000000 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt Nginx Tests""" diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index e53bef059..b94b7f69f 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -1,39 +1,38 @@ +import codecs +import os import sys from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() -# Please update tox.ini when modifying dependency version requirements + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around certbot-nginx install_requires = [ - 'acme=={0}'.format(version), + 'certbot-nginx', 'letsencrypt=={0}'.format(version), - 'PyOpenSSL', - 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? - # For pkg_resources. >=1.0 so pip resolves it to a version cryptography - # will tolerate; see #2599: - 'setuptools>=1.0', - 'zope.interface', ] -if sys.version_info < (2, 7): - install_requires.append('mock<1.1.0') -else: - install_requires.append('mock') - -docs_extras = [ - 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags - 'sphinx_rtd_theme', -] setup( name='letsencrypt-nginx', version=version, - description="Nginx plugin for Let's Encrypt client", + description="Nginx plugin for Let's Encrypt", + long_description=readme, url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ @@ -57,13 +56,4 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, - extras_require={ - 'docs': docs_extras, - }, - entry_points={ - 'letsencrypt.plugins': [ - 'nginx = letsencrypt_nginx.configurator:NginxConfigurator', - ], - }, - test_suite='letsencrypt_nginx', ) diff --git a/letsencrypt/LICENSE.txt b/letsencrypt/LICENSE.txt new file mode 100644 index 000000000..82d868261 --- /dev/null +++ b/letsencrypt/LICENSE.txt @@ -0,0 +1,205 @@ +Let's Encrypt ACME Client +Copyright (c) Electronic Frontier Foundation and others +Licensed Apache Version 2.0 + +The nginx plugin incorporates code from nginxparser +Copyright (c) 2014 Fatih Erikli +Licensed MIT + + +Text of Apache License +====================== + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + +Text of MIT License +=================== +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/letsencrypt/MANIFEST.in b/letsencrypt/MANIFEST.in new file mode 100644 index 000000000..97e2ad3df --- /dev/null +++ b/letsencrypt/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE.txt +include README.rst diff --git a/letsencrypt/README.rst b/letsencrypt/README.rst new file mode 100644 index 000000000..b5fa0ec95 --- /dev/null +++ b/letsencrypt/README.rst @@ -0,0 +1,2 @@ +This package is a simple shim around the ``certbot`` ACME client for backwards +compatibility. diff --git a/letsencrypt/display/__init__.py b/letsencrypt/display/__init__.py deleted file mode 100644 index 01e3ca11f..000000000 --- a/letsencrypt/display/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt display utilities.""" diff --git a/letsencrypt/letsencrypt/__init__.py b/letsencrypt/letsencrypt/__init__.py new file mode 100644 index 000000000..a67d641f5 --- /dev/null +++ b/letsencrypt/letsencrypt/__init__.py @@ -0,0 +1,8 @@ +"""Let's Encrypt ACME client.""" +import sys + + +import certbot + + +sys.modules['letsencrypt'] = certbot diff --git a/letsencrypt/plugins/__init__.py b/letsencrypt/plugins/__init__.py deleted file mode 100644 index 538189015..000000000 --- a/letsencrypt/plugins/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt client.plugins.""" diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py deleted file mode 100644 index 6d2899511..000000000 --- a/letsencrypt/plugins/webroot.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Webroot plugin.""" -import errno -import logging -import os -from collections import defaultdict - -import zope.interface -import six - -from acme import challenges - -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.plugins import common - - -logger = logging.getLogger(__name__) - - -@zope.interface.implementer(interfaces.IAuthenticator) -@zope.interface.provider(interfaces.IPluginFactory) -class Authenticator(common.Plugin): - """Webroot Authenticator.""" - - description = "Webroot Authenticator" - - MORE_INFO = """\ -Authenticator plugin that performs http-01 challenge by saving -necessary validation resources to appropriate paths on the file -system. It expects that there is some other HTTP server configured -to serve all files under specified web root ({0}).""" - - def more_info(self): # pylint: disable=missing-docstring,no-self-use - return self.MORE_INFO.format(self.conf("path")) - - @classmethod - def add_parser_arguments(cls, add): - # --webroot-path and --webroot-map are added in cli.py because they - # are parsed in conjunction with --domains - pass - - def get_chall_pref(self, domain): # pragma: no cover - # pylint: disable=missing-docstring,no-self-use,unused-argument - return [challenges.HTTP01] - - def __init__(self, *args, **kwargs): - super(Authenticator, self).__init__(*args, **kwargs) - self.full_roots = {} - self.performed = defaultdict(set) - - def prepare(self): # pylint: disable=missing-docstring - path_map = self.conf("map") - - if not path_map: - raise errors.PluginError( - "Missing parts of webroot configuration; please set either " - "--webroot-path and --domains, or --webroot-map. Run with " - " --help webroot for examples.") - for name, path in path_map.items(): - if not os.path.isdir(path): - raise errors.PluginError(path + " does not exist or is not a directory") - self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) - - logger.debug("Creating root challenges validation dir at %s", - self.full_roots[name]) - - # Change the permissions to be writable (GH #1389) - # Umask is used instead of chmod to ensure the client can also - # run as non-root (GH #1795) - old_umask = os.umask(0o022) - - try: - # This is coupled with the "umask" call above because - # os.makedirs's "mode" parameter may not always work: - # https://stackoverflow.com/questions/5231901/permission-problems-when-creating-a-dir-with-os-makedirs-python - os.makedirs(self.full_roots[name], 0o0755) - - # Set owner as parent directory if possible - try: - stat_path = os.stat(path) - os.chown(self.full_roots[name], stat_path.st_uid, - stat_path.st_gid) - except OSError as exception: - if exception.errno == errno.EACCES: - logger.debug("Insufficient permissions to change owner and uid - ignoring") - else: - raise errors.PluginError( - "Couldn't create root for {0} http-01 " - "challenge responses: {1}", name, exception) - - except OSError as exception: - if exception.errno != errno.EEXIST: - raise errors.PluginError( - "Couldn't create root for {0} http-01 " - "challenge responses: {1}", name, exception) - finally: - os.umask(old_umask) - - def perform(self, achalls): # pylint: disable=missing-docstring - assert self.full_roots, "Webroot plugin appears to be missing webroot map" - return [self._perform_single(achall) for achall in achalls] - - def _get_root_path(self, achall): - try: - path = self.full_roots[achall.domain] - except KeyError: - raise errors.PluginError("Missing --webroot-path for domain: {0}" - .format(achall.domain)) - if not os.path.exists(path): - raise errors.PluginError("Mysteriously missing path {0} for domain: {1}" - .format(path, achall.domain)) - return path - - def _get_validation_path(self, root_path, achall): - return os.path.join(root_path, achall.chall.encode("token")) - - def _perform_single(self, achall): - response, validation = achall.response_and_validation() - - root_path = self._get_root_path(achall) - validation_path = self._get_validation_path(root_path, achall) - logger.debug("Attempting to save validation to %s", validation_path) - - # Change permissions to be world-readable, owner-writable (GH #1795) - old_umask = os.umask(0o022) - - try: - with open(validation_path, "w") as validation_file: - validation_file.write(validation.encode()) - finally: - os.umask(old_umask) - - self.performed[root_path].add(achall) - - return response - - def cleanup(self, achalls): # pylint: disable=missing-docstring - for achall in achalls: - root_path = self._get_root_path(achall) - validation_path = self._get_validation_path(root_path, achall) - logger.debug("Removing %s", validation_path) - os.remove(validation_path) - self.performed[root_path].remove(achall) - - for root_path, achalls in six.iteritems(self.performed): - if not achalls: - try: - os.rmdir(root_path) - logger.debug("All challenges cleaned up, removing %s", - root_path) - except OSError as exc: - if exc.errno == errno.ENOTEMPTY: - logger.debug("Challenges cleaned up but %s not empty", - root_path) - elif exc.errno == errno.EACCES: - logger.debug("Challenges cleaned up but no permissions for %s", - root_path) - else: - raise diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py deleted file mode 100644 index ed0326555..000000000 --- a/letsencrypt/plugins/webroot_test.py +++ /dev/null @@ -1,187 +0,0 @@ -"""Tests for letsencrypt.plugins.webroot.""" - -from __future__ import print_function - -import errno -import os -import shutil -import stat -import tempfile -import unittest - -import mock - -from acme import challenges -from acme import jose - -from letsencrypt import achallenges -from letsencrypt import errors - -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util - - -KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) - - -class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.webroot.Authenticator.""" - - achall = achallenges.KeyAuthorizationAnnotatedChallenge( - challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) - - def setUp(self): - from letsencrypt.plugins.webroot import Authenticator - self.path = tempfile.mkdtemp() - self.root_challenge_path = os.path.join( - self.path, ".well-known", "acme-challenge") - self.validation_path = os.path.join( - self.root_challenge_path, - "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") - self.config = mock.MagicMock(webroot_path=self.path, - webroot_map={"thing.com": self.path}) - self.auth = Authenticator(self.config, "webroot") - - def tearDown(self): - shutil.rmtree(self.path) - - def test_more_info(self): - more_info = self.auth.more_info() - self.assertTrue(isinstance(more_info, str)) - self.assertTrue(self.path in more_info) - - def test_add_parser_arguments(self): - add = mock.MagicMock() - self.auth.add_parser_arguments(add) - self.assertEqual(0, add.call_count) # args moved to cli.py! - - def test_prepare_bad_root(self): - self.config.webroot_path = os.path.join(self.path, "null") - self.config.webroot_map["thing.com"] = self.config.webroot_path - self.assertRaises(errors.PluginError, self.auth.prepare) - - def test_prepare_missing_root(self): - self.config.webroot_path = None - self.config.webroot_map = {} - self.assertRaises(errors.PluginError, self.auth.prepare) - - def test_prepare_full_root_exists(self): - # prepare() has already been called once in setUp() - self.auth.prepare() # shouldn't raise any exceptions - - def test_prepare_reraises_other_errors(self): - self.auth.full_path = os.path.join(self.path, "null") - permission_canary = os.path.join(self.path, "rnd") - with open(permission_canary, "w") as f: - f.write("thingimy") - os.chmod(self.path, 0o000) - try: - open(permission_canary, "r") - print("Warning, running tests as root skips permissions tests...") - except IOError: - # ok, permissions work, test away... - self.assertRaises(errors.PluginError, self.auth.prepare) - os.chmod(self.path, 0o700) - - @mock.patch("letsencrypt.plugins.webroot.os.chown") - def test_failed_chown_eacces(self, mock_chown): - mock_chown.side_effect = OSError(errno.EACCES, "msg") - self.auth.prepare() # exception caught and logged - - @mock.patch("letsencrypt.plugins.webroot.os.chown") - def test_failed_chown_not_eacces(self, mock_chown): - mock_chown.side_effect = OSError() - self.assertRaises(errors.PluginError, self.auth.prepare) - - def test_prepare_permissions(self): - self.auth.prepare() - - # Remove exec bit from permission check, so that it - # matches the file - self.auth.perform([self.achall]) - path_permissions = stat.S_IMODE(os.stat(self.validation_path).st_mode) - self.assertEqual(path_permissions, 0o644) - - # Check permissions of the directories - - for dirpath, dirnames, _ in os.walk(self.path): - for directory in dirnames: - full_path = os.path.join(dirpath, directory) - dir_permissions = stat.S_IMODE(os.stat(full_path).st_mode) - self.assertEqual(dir_permissions, 0o755) - - parent_gid = os.stat(self.path).st_gid - parent_uid = os.stat(self.path).st_uid - - self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid) - self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid) - - def test_perform_missing_path(self): - self.auth.prepare() - - missing_achall = achallenges.KeyAuthorizationAnnotatedChallenge( - challb=acme_util.HTTP01_P, domain="thing2.com", account_key=KEY) - self.assertRaises( - errors.PluginError, self.auth.perform, [missing_achall]) - - self.auth.full_roots[self.achall.domain] = 'null' - self.assertRaises( - errors.PluginError, self.auth.perform, [self.achall]) - - def test_perform_cleanup(self): - self.auth.prepare() - responses = self.auth.perform([self.achall]) - self.assertEqual(1, len(responses)) - self.assertTrue(os.path.exists(self.validation_path)) - with open(self.validation_path) as validation_f: - validation = validation_f.read() - self.assertTrue( - challenges.KeyAuthorizationChallengeResponse( - key_authorization=validation).verify( - self.achall.chall, KEY.public_key())) - - self.auth.cleanup([self.achall]) - self.assertFalse(os.path.exists(self.validation_path)) - self.assertFalse(os.path.exists(self.root_challenge_path)) - - def test_cleanup_leftovers(self): - self.auth.prepare() - self.auth.perform([self.achall]) - - leftover_path = os.path.join(self.root_challenge_path, 'leftover') - os.mkdir(leftover_path) - - self.auth.cleanup([self.achall]) - self.assertFalse(os.path.exists(self.validation_path)) - self.assertTrue(os.path.exists(self.root_challenge_path)) - - os.rmdir(leftover_path) - - @mock.patch('os.rmdir') - def test_cleanup_permission_denied(self, mock_rmdir): - self.auth.prepare() - self.auth.perform([self.achall]) - - os_error = OSError() - os_error.errno = errno.EACCES - mock_rmdir.side_effect = os_error - - self.auth.cleanup([self.achall]) - self.assertFalse(os.path.exists(self.validation_path)) - self.assertTrue(os.path.exists(self.root_challenge_path)) - - @mock.patch('os.rmdir') - def test_cleanup_oserror(self, mock_rmdir): - self.auth.prepare() - self.auth.perform([self.achall]) - - os_error = OSError() - os_error.errno = errno.ENOENT - mock_rmdir.side_effect = os_error - - self.assertRaises(OSError, self.auth.cleanup, [self.achall]) - self.assertFalse(os.path.exists(self.validation_path)) - self.assertTrue(os.path.exists(self.root_challenge_path)) - -if __name__ == "__main__": - unittest.main() # pragma: no cover diff --git a/letsencrypt/setup.py b/letsencrypt/setup.py new file mode 100644 index 000000000..708c31f4b --- /dev/null +++ b/letsencrypt/setup.py @@ -0,0 +1,62 @@ +import codecs +import os +import sys + +from setuptools import setup +from setuptools import find_packages + + +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() + + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +# This package is a simple shim around certbot +install_requires = ['certbot'] + + +version = '0.6.0.dev0' + + +setup( + name='letsencrypt', + version=version, + description="ACME client", + long_description=readme, + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Environment :: Console :: Curses', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + entry_points={ + 'console_scripts': [ + 'letsencrypt = certbot.main:main', + ], + }, +) diff --git a/letsencrypt/tests/__init__.py b/letsencrypt/tests/__init__.py deleted file mode 100644 index d9db68022..000000000 --- a/letsencrypt/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt Tests""" diff --git a/letsencrypt/tests/display/__init__.py b/letsencrypt/tests/display/__init__.py deleted file mode 100644 index 79a386ea2..000000000 --- a/letsencrypt/tests/display/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt Display Tests""" diff --git a/letshelp-certbot/LICENSE.txt b/letshelp-certbot/LICENSE.txt new file mode 100644 index 000000000..981c46c9f --- /dev/null +++ b/letshelp-certbot/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letshelp-certbot/MANIFEST.in b/letshelp-certbot/MANIFEST.in new file mode 100644 index 000000000..623392f28 --- /dev/null +++ b/letshelp-certbot/MANIFEST.in @@ -0,0 +1,4 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include letshelp_certbot/testdata * diff --git a/letshelp-certbot/README.rst b/letshelp-certbot/README.rst new file mode 100644 index 000000000..bbe2f2570 --- /dev/null +++ b/letshelp-certbot/README.rst @@ -0,0 +1 @@ +Let's help Certbot client diff --git a/letshelp-letsencrypt/docs/.gitignore b/letshelp-certbot/docs/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/.gitignore rename to letshelp-certbot/docs/.gitignore diff --git a/letshelp-letsencrypt/docs/Makefile b/letshelp-certbot/docs/Makefile similarity index 97% rename from letshelp-letsencrypt/docs/Makefile rename to letshelp-certbot/docs/Makefile index 8e742d837..4b392ab8d 100644 --- a/letshelp-letsencrypt/docs/Makefile +++ b/letshelp-certbot/docs/Makefile @@ -87,9 +87,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letshelp-letsencrypt.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letshelp-certbot.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-letsencrypt.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-certbot.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/letshelp-letsencrypt" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letshelp-letsencrypt" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letshelp-certbot" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letshelp-certbot" @echo "# devhelp" epub: diff --git a/letshelp-letsencrypt/docs/_static/.gitignore b/letshelp-certbot/docs/_static/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/_static/.gitignore rename to letshelp-certbot/docs/_static/.gitignore diff --git a/letshelp-letsencrypt/docs/_templates/.gitignore b/letshelp-certbot/docs/_templates/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/_templates/.gitignore rename to letshelp-certbot/docs/_templates/.gitignore diff --git a/letshelp-letsencrypt/docs/api.rst b/letshelp-certbot/docs/api.rst similarity index 100% rename from letshelp-letsencrypt/docs/api.rst rename to letshelp-certbot/docs/api.rst diff --git a/letshelp-certbot/docs/api/index.rst b/letshelp-certbot/docs/api/index.rst new file mode 100644 index 000000000..5ced5f501 --- /dev/null +++ b/letshelp-certbot/docs/api/index.rst @@ -0,0 +1,11 @@ +:mod:`letshelp_certbot` +--------------------------- + +.. automodule:: letshelp_certbot + :members: + +:mod:`letshelp_certbot.apache` +================================== + +.. automodule:: letshelp_certbot.apache + :members: diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-certbot/docs/conf.py similarity index 93% rename from letshelp-letsencrypt/docs/conf.py rename to letshelp-certbot/docs/conf.py index a84c4c982..905d70662 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-certbot/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letshelp-letsencrypt documentation build configuration file, created by +# letshelp-certbot documentation build configuration file, created by # sphinx-quickstart on Sun Oct 18 13:40:19 2015. # # This file is execfile()d with the current directory set to its @@ -58,9 +58,9 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letshelp-letsencrypt' +project = u'letshelp-certbot' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -220,7 +220,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letshelp-letsencryptdoc' +htmlhelp_basename = 'letshelp-certbotdoc' # -- Options for LaTeX output --------------------------------------------- @@ -242,8 +242,8 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letshelp-letsencrypt.tex', u'letshelp-letsencrypt Documentation', - u'Let\'s Encrypt Project', 'manual'), + (master_doc, 'letshelp-certbot.tex', u'letshelp-certbot Documentation', + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -272,7 +272,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'letshelp-letsencrypt', u'letshelp-letsencrypt Documentation', + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', [author], 1) ] @@ -286,8 +286,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'letshelp-letsencrypt', u'letshelp-letsencrypt Documentation', - author, 'letshelp-letsencrypt', 'One line description of project.', + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', + author, 'letshelp-certbot', 'One line description of project.', 'Miscellaneous'), ] @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letshelp-letsencrypt/docs/index.rst b/letshelp-certbot/docs/index.rst similarity index 77% rename from letshelp-letsencrypt/docs/index.rst rename to letshelp-certbot/docs/index.rst index 6b67a2e1f..678d9be2e 100644 --- a/letshelp-letsencrypt/docs/index.rst +++ b/letshelp-certbot/docs/index.rst @@ -1,9 +1,9 @@ -.. letshelp-letsencrypt documentation master file, created by +.. letshelp-certbot documentation master file, created by sphinx-quickstart on Sun Oct 18 13:40:19 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to letshelp-letsencrypt's documentation! +Welcome to letshelp-certbot's documentation! ================================================ Contents: diff --git a/letshelp-letsencrypt/docs/make.bat b/letshelp-certbot/docs/make.bat similarity index 97% rename from letshelp-letsencrypt/docs/make.bat rename to letshelp-certbot/docs/make.bat index 006f7825d..0229b4f69 100644 --- a/letshelp-letsencrypt/docs/make.bat +++ b/letshelp-certbot/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letshelp-letsencrypt.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letshelp-certbot.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-letsencrypt.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-certbot.ghc goto end ) diff --git a/letshelp-certbot/letshelp_certbot/__init__.py b/letshelp-certbot/letshelp_certbot/__init__.py new file mode 100644 index 000000000..6882a19d4 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/__init__.py @@ -0,0 +1 @@ +"""Tools for submitting server configurations""" diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/apache.py b/letshelp-certbot/letshelp_certbot/apache.py similarity index 96% rename from letshelp-letsencrypt/letshelp_letsencrypt/apache.py rename to letshelp-certbot/letshelp_certbot/apache.py index d7cb05b70..5752bdab0 100755 --- a/letshelp-letsencrypt/letshelp_letsencrypt/apache.py +++ b/letshelp-certbot/letshelp_certbot/apache.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""Let's Encrypt Apache configuration submission script""" +"""Certbot Apache configuration submission script""" from __future__ import print_function @@ -17,12 +17,12 @@ import textwrap _DESCRIPTION = """ -Let's Help is a simple script you can run to help out the Let's Encrypt -project. Since Let's Encrypt will support automatically configuring HTTPS on +Let's Help is a simple script you can run to help out the Certbot +project. Since Certbot will support automatically configuring HTTPS on many servers, we want to test this functionality on as many configurations as possible. This script will create a sanitized copy of your Apache configuration, notifying you of the files that have been selected. If (and only -if) you approve this selection, these files will be sent to the Let's Encrypt +if) you approve this selection, these files will be sent to the Certbot developers. """ @@ -38,8 +38,9 @@ argument and the path to the binary. # Keywords likely to be found in filenames of sensitive files _SENSITIVE_FILENAME_REGEX = re.compile(r"^(?!.*proxy_fdpass).*pass.*$|private|" - r"secret|cert|crt|key|rsa|dsa|pw|\.pem|" - r"\.der|\.p12|\.pfx|\.p7b") + r"secret|^(?!.*certbot).*cert.*$|crt|" + r"key|rsa|dsa|pw|\.pem|\.der|\.p12|" + r"\.pfx|\.p7b") def make_and_verify_selection(server_root, temp_dir): diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py b/letshelp-certbot/letshelp_certbot/apache_test.py similarity index 98% rename from letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py rename to letshelp-certbot/letshelp_certbot/apache_test.py index 7ed1df760..0c1b5f2f6 100644 --- a/letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py +++ b/letshelp-certbot/letshelp_certbot/apache_test.py @@ -1,4 +1,4 @@ -"""Tests for letshelp.letshelp_letsencrypt_apache.py""" +"""Tests for letshelp.letshelp_certbot_apache.py""" import argparse import functools import os @@ -10,7 +10,7 @@ import unittest import mock -import letshelp_letsencrypt.apache as letshelp_le_apache +import letshelp_certbot.apache as letshelp_le_apache _PARTIAL_CONF_PATH = os.path.join("mods-available", "ssl.load") @@ -25,7 +25,7 @@ _SECRET_FILE = pkg_resources.resource_filename( __name__, os.path.join("testdata", "super_secret_file.txt")) -_MODULE_NAME = "letshelp_letsencrypt.apache" +_MODULE_NAME = "letshelp_certbot.apache" _COMPILE_SETTINGS = """Server version: Apache/2.4.10 (Debian) diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-available/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-available/ssl.load rename to letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-enabled/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-enabled/ssl.load rename to letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/super_secret_file.txt b/letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/super_secret_file.txt rename to letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_k3y b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_k3y rename to letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_p4sswd b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_p4sswd rename to letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd diff --git a/letshelp-letsencrypt/readthedocs.org.requirements.txt b/letshelp-certbot/readthedocs.org.requirements.txt similarity index 94% rename from letshelp-letsencrypt/readthedocs.org.requirements.txt rename to letshelp-certbot/readthedocs.org.requirements.txt index 898d2716e..7858b312f 100644 --- a/letshelp-letsencrypt/readthedocs.org.requirements.txt +++ b/letshelp-certbot/readthedocs.org.requirements.txt @@ -7,4 +7,4 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead --e letshelp-letsencrypt[docs] +-e letshelp-certbot[docs] diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py new file mode 100644 index 000000000..8359d2766 --- /dev/null +++ b/letshelp-certbot/setup.py @@ -0,0 +1,59 @@ +import sys + +from setuptools import setup +from setuptools import find_packages + + +version = '0.6.0.dev0' + +install_requires = [ + 'setuptools', # pkg_resources +] +if sys.version_info < (2, 7): + install_requires.append('mock<1.1.0') +else: + install_requires.append('mock') + +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + +setup( + name='letshelp-certbot', + version=version, + description="Let's help Certbot client", + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, + entry_points={ + 'console_scripts': [ + 'letshelp-certbot-apache = letshelp_certbot.apache:main', + ], + }, + test_suite='letshelp_certbot', +) diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in index 6ea55a950..97e2ad3df 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-letsencrypt/MANIFEST.in @@ -1,4 +1,2 @@ include LICENSE.txt include README.rst -recursive-include docs * -recursive-include letshelp_letsencrypt/testdata * diff --git a/letshelp-letsencrypt/README.rst b/letshelp-letsencrypt/README.rst index 159048d6d..57d0d8a3b 100644 --- a/letshelp-letsencrypt/README.rst +++ b/letshelp-letsencrypt/README.rst @@ -1 +1,2 @@ -Let's help Let's Encrypt client +This package is a simple shim around the ``letshelp-certbot`` for backwards +compatibility. diff --git a/letshelp-letsencrypt/docs/api/index.rst b/letshelp-letsencrypt/docs/api/index.rst deleted file mode 100644 index 8f6872eac..000000000 --- a/letshelp-letsencrypt/docs/api/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -:mod:`letshelp_letsencrypt` ---------------------------- - -.. automodule:: letshelp_letsencrypt - :members: - -:mod:`letshelp_letsencrypt.apache` -================================== - -.. automodule:: letshelp_letsencrypt.apache - :members: diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py b/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py index 6882a19d4..fe4e272f9 100644 --- a/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py +++ b/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py @@ -1 +1,8 @@ -"""Tools for submitting server configurations""" +"""Tools for submitting server configurations.""" +import sys + + +import letshelp_certbot + + +sys.modules['letshelp_letsencrypt'] = letshelp_certbot diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index fff8dcfc3..875c6fc92 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -1,34 +1,40 @@ +import codecs +import os import sys from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() -install_requires = [ - 'setuptools', # pkg_resources -] -if sys.version_info < (2, 7): - install_requires.append('mock<1.1.0') -else: - install_requires.append('mock') -docs_extras = [ - 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags - 'sphinx_rtd_theme', -] +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around letshelp-certbot +install_requires = ['letshelp-certbot'] + setup( name='letshelp-letsencrypt', version=version, description="Let's help Let's Encrypt client", + long_description=readme, url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: Apache Software License', 'Operating System :: POSIX :: Linux', @@ -47,13 +53,9 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, - extras_require={ - 'docs': docs_extras, - }, entry_points={ 'console_scripts': [ - 'letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main', + 'letshelp-letsencrypt-apache = letshelp_certbot.apache:main', ], }, - test_suite='letshelp_letsencrypt', ) diff --git a/linter_plugin.py b/linter_plugin.py index 9a165d81f..4938755cf 100644 --- a/linter_plugin.py +++ b/linter_plugin.py @@ -1,4 +1,4 @@ -"""Let's Encrypt ACME PyLint plugin. +"""Certbot ACME PyLint plugin. http://docs.pylint.org/plugins.html diff --git a/pep8.travis.sh b/pep8.travis.sh index 91124bdbd..c13547a78 100755 --- a/pep8.travis.sh +++ b/pep8.travis.sh @@ -7,11 +7,11 @@ pep8 --config=acme/.pep8 acme pep8 \ setup.py \ - letsencrypt \ - letsencrypt-apache \ - letsencrypt-nginx \ - letsencrypt-compatibility-test \ - letshelp-letsencrypt \ + certbot \ + certbot-apache \ + certbot-nginx \ + certbot-compatibility-test \ + letshelp-certbot \ || echo "PEP8 checking failed, but it's ignored in Travis" # echo exits with 0 diff --git a/setup.cfg b/setup.cfg index 1ea06661e..8d68bac30 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,6 +3,6 @@ zip_ok = false [nosetests] nocapture=1 -cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx +cover-package=certbot,acme,certbot_apache,certbot_nginx cover-erase=1 cover-tests=1 diff --git a/setup.py b/setup.py index 87cef2cb2..67cefdc48 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read_file(filename, encoding='utf8'): here = os.path.abspath(os.path.dirname(__file__)) # read version number (and other metadata) from package init -init_fn = os.path.join(here, 'letsencrypt', '__init__.py') +init_fn = os.path.join(here, 'certbot', '__init__.py') meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", read_file(init_fn))) readme = read_file(os.path.join(here, 'README.rst')) @@ -85,12 +85,12 @@ docs_extras = [ ] setup( - name='letsencrypt', + name='certbot', version=version, - description="Let's Encrypt client", + description="ACME client", long_description=readme, # later: + '\n\n' + changes url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ @@ -122,18 +122,18 @@ setup( }, # to test all packages run "python setup.py test -s - # {acme,letsencrypt_apache,letsencrypt_nginx}" - test_suite='letsencrypt', + # {acme,certbot_apache,certbot_nginx}" + test_suite='certbot', entry_points={ 'console_scripts': [ - 'letsencrypt = letsencrypt.main:main', + 'certbot = certbot.main:main', ], - 'letsencrypt.plugins': [ - 'manual = letsencrypt.plugins.manual:Authenticator', - 'null = letsencrypt.plugins.null:Installer', - 'standalone = letsencrypt.plugins.standalone:Authenticator', - 'webroot = letsencrypt.plugins.webroot:Authenticator', + 'certbot.plugins': [ + 'manual = certbot.plugins.manual:Authenticator', + 'null = certbot.plugins.null:Installer', + 'standalone = certbot.plugins.standalone:Authenticator', + 'webroot = certbot.plugins.webroot:Authenticator', ], }, ) diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 77e866b52..201343525 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -4,7 +4,7 @@ # instance (see ./boulder-start.sh). # # Environment variables: -# SERVER: Passed as "letsencrypt --server" argument. +# SERVER: Passed as "certbot --server" argument. # # Note: this script is called by Boulder integration test suite! @@ -21,7 +21,7 @@ else fi common_no_force_renew() { - letsencrypt_test_no_force_renew \ + certbot_test_no_force_renew \ --authenticator standalone \ --installer null \ "$@" @@ -94,5 +94,5 @@ common revoke --cert-path "$root/conf/live/le2.wtf/cert.pem" \ if type nginx; then - . ./letsencrypt-nginx/tests/boulder-integration.sh + . ./certbot-nginx/tests/boulder-integration.sh fi diff --git a/tests/display.py b/tests/display.py index dff56e42e..ecb7c279b 100644 --- a/tests/display.py +++ b/tests/display.py @@ -1,8 +1,8 @@ """Manual test of display functions.""" import sys -from letsencrypt.display import util -from letsencrypt.tests.display import util_test +from certbot.display import util +from certbot.tests.display import util_test def test_visual(displayer, choices): diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index e86d087cb..8992a18c0 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -11,14 +11,14 @@ store_flags="--config-dir $root/conf --work-dir $root/work" store_flags="$store_flags --logs-dir $root/logs" export root store_flags -letsencrypt_test () { - letsencrypt_test_no_force_renew \ +certbot_test () { + certbot_test_no_force_renew \ --renew-by-default \ "$@" } -letsencrypt_test_no_force_renew () { - letsencrypt \ +certbot_test_no_force_renew () { + certbot \ --server "${SERVER:-http://localhost:4000/directory}" \ --no-verify-ssl \ --tls-sni-01-port 5001 \ diff --git a/tests/letstest/README.md b/tests/letstest/README.md index a085e9d91..a9b4db6b5 100644 --- a/tests/letstest/README.md +++ b/tests/letstest/README.md @@ -1,10 +1,10 @@ # letstest -simple aws testfarm scripts for letsencrypt client testing +simple aws testfarm scripts for certbot client testing - Configures (canned) boulder server - Launches EC2 instances with a given list of AMIs for different distros -- Copies letsencrypt repo and puts it on the instances -- Runs letsencrypt tests (bash scripts) on all of these +- Copies certbot repo and puts it on the instances +- Runs certbot tests (bash scripts) on all of these - Logs execution and success/fail for debugging ## Notes diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 876b7807f..02dfc4410 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -1,10 +1,10 @@ """ -Letsencrypt Integration Test Tool +Certbot Integration Test Tool - Configures (canned) boulder server - Launches EC2 instances with a given list of AMIs for different distros -- Copies letsencrypt repo and puts it on the instances -- Runs letsencrypt tests (bash scripts) on all of these +- Copies certbot repo and puts it on the instances +- Runs certbot tests (bash scripts) on all of these - Logs execution and success/fail for debugging Notes: @@ -61,10 +61,10 @@ parser.add_argument('test_script', # required=False) parser.add_argument('--repo', default='https://github.com/letsencrypt/letsencrypt.git', - help='letsencrypt git repo to use') + help='certbot git repo to use') parser.add_argument('--branch', default='~', - help='letsencrypt git branch to trial') + help='certbot git branch to trial') parser.add_argument('--pull_request', default='~', help='letsencrypt/letsencrypt pull request to trial') @@ -257,7 +257,7 @@ def local_git_PR(repo_url, PRnumstr, merge_master=True): local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') local('git clone %s letsencrypt'% repo_url) local('cd letsencrypt && git fetch origin pull/%s/head:lePRtest'%PRnumstr) - local('cd letsencrypt && git co lePRtest') + local('cd letsencrypt && git checkout lePRtest') if merge_master: local('cd letsencrypt && git remote update origin') local('cd letsencrypt && git merge origin/master -m "testmerge"') @@ -291,7 +291,7 @@ def config_and_launch_boulder(instance): execute(deploy_script, 'scripts/boulder_config.sh') execute(run_boulder) -def install_and_launch_letsencrypt(instance, boulder_url, target): +def install_and_launch_certbot(instance, boulder_url, target): execute(local_repo_to_remote) with shell_env(BOULDER_URL=boulder_url, PUBLIC_IP=instance.public_ip_address, @@ -301,13 +301,13 @@ def install_and_launch_letsencrypt(instance, boulder_url, target): OS_TYPE=target['type']): execute(deploy_script, cl_args.test_script) -def grab_letsencrypt_log(): +def grab_certbot_log(): "grabs letsencrypt.log via cat into logged stdout" sudo('if [ -f /var/log/letsencrypt/letsencrypt.log ]; then \ cat /var/log/letsencrypt/letsencrypt.log; else echo "[novarlog]"; fi') # fallback file if /var/log is unwriteable...? correct? - sudo('if [ -f ./letsencrypt.log ]; then \ - cat ./letsencrypt.log; else echo "[nolocallog]"; fi') + sudo('if [ -f ./certbot.log ]; then \ + cat ./certbot.log; else echo "[nolocallog]"; fi') def create_client_instances(targetlist): "Create a fleet of client instances" @@ -357,10 +357,10 @@ def test_client_process(inqueue, outqueue): print("%s - %s FAIL"%(target['ami'], target['name'])) pass - # append server letsencrypt.log to each per-machine output log - print("\n\nletsencrypt.log\n" + "-"*80 + "\n") + # append server certbot.log to each per-machine output log + print("\n\ncertbot.log\n" + "-"*80 + "\n") try: - execute(grab_letsencrypt_log) + execute(grab_certbot_log) except: print("log fail\n") pass diff --git a/tests/letstest/scripts/test_apache2.sh b/tests/letstest/scripts/test_apache2.sh index 940cc36c6..3e0846216 100755 --- a/tests/letstest/scripts/test_apache2.sh +++ b/tests/letstest/scripts/test_apache2.sh @@ -22,8 +22,8 @@ then sudo chmod -R oug+rwx /var/www sudo chmod -R oug+rw /etc/httpd sudo echo 'foobar' > /var/www/$PUBLIC_HOSTNAME/public_html/index.html - sudo mkdir /etc/httpd/sites-available #letsencrypt requires this... - sudo mkdir /etc/httpd/sites-enabled #letsencrypt requires this... + sudo mkdir /etc/httpd/sites-available #certbot requires this... + sudo mkdir /etc/httpd/sites-enabled #certbot requires this... #sudo echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf sudo echo """ @@ -35,7 +35,7 @@ then #sudo cp /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf /etc/httpd/sites-enabled/ fi -# Run letsencrypt-apache2. +# Run certbot-apache2. cd letsencrypt echo "Bootstrapping dependencies..." @@ -45,7 +45,7 @@ if [ $? -ne 0 ] ; then fi tools/venv.sh -sudo venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ +sudo venv/bin/certbot -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL if [ $? -ne 0 ] ; then diff --git a/tests/letstest/scripts/test_renew_standalone.sh b/tests/letstest/scripts/test_renew_standalone.sh index d90ae9ab6..31c38ea46 100755 --- a/tests/letstest/scripts/test_renew_standalone.sh +++ b/tests/letstest/scripts/test_renew_standalone.sh @@ -3,7 +3,7 @@ # $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL # are dynamically set at execution -# run letsencrypt-apache2 via letsencrypt-auto +# run certbot-apache2 via letsencrypt-auto cd letsencrypt export SUDO=sudo @@ -14,12 +14,12 @@ elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO bootstrap/_rpm_common.sh else - echo "Dont have bootstrapping for this OS!" + echo "Don't have bootstrapping for this OS!" exit 1 fi bootstrap/dev/venv.sh -sudo venv/bin/letsencrypt certonly --debug --standalone -t --agree-dev-preview --agree-tos \ +sudo venv/bin/certbot certonly --debug --standalone -t --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL -v if [ $? -ne 0 ] ; then @@ -36,7 +36,7 @@ if [ $? -ne 0 ] ; then FAIL=1 fi -sudo venv/bin/letsencrypt renew --renew-by-default +sudo venv/bin/certbot renew --renew-by-default if [ $? -ne 0 ] ; then FAIL=1 diff --git a/tests/travis-integration.sh b/tests/travis-integration.sh index 3b507bb86..1b51f0980 100755 --- a/tests/travis-integration.sh +++ b/tests/travis-integration.sh @@ -11,9 +11,9 @@ export LETSENCRYPT_PATH=`pwd` cd $GOPATH/src/github.com/letsencrypt/boulder/ # boulder's integration-test.py has code that knows to start and wait for the -# boulder processes to start reliably and then will run the letsencrypt +# boulder processes to start reliably and then will run the certbot # boulder-interation.sh on its own. The --letsencrypt flag says to run only the -# letsencrypt tests (instead of any other client tests it might run). We're +# certbot tests (instead of any other client tests it might run). We're # going to want to define a more robust interaction point between the boulder -# and letsencrypt tests, but that will be better built off of this. +# and certbot tests, but that will be better built off of this. python test/integration-test.py --letsencrypt diff --git a/tools/_venv_common.sh b/tools/_venv_common.sh index d07f38ed8..a121af82d 100755 --- a/tools/_venv_common.sh +++ b/tools/_venv_common.sh @@ -3,8 +3,8 @@ VENV_NAME=${VENV_NAME:-venv} # .egg-info directories tend to cause bizzaire problems (e.g. `pip -e -# .` might unexpectedly install letshelp-letsencrypt only, in case -# `python letshelp-letsencrypt/setup.py build` has been called +# .` might unexpectedly install letshelp-certbot only, in case +# `python letshelp-certbot/setup.py build` has been called # earlier) rm -rf *.egg-info diff --git a/tools/deps.sh b/tools/deps.sh index 6fb2bf63b..e12f201a5 100755 --- a/tools/deps.sh +++ b/tools/deps.sh @@ -2,9 +2,9 @@ # # Find all Python imports. # -# ./tools/deps.sh letsencrypt +# ./tools/deps.sh certbot # ./tools/deps.sh acme -# ./tools/deps.sh letsencrypt-apache +# ./tools/deps.sh certbot-apache # ... # # Manually compare the output with deps in setup.py. diff --git a/tools/release.sh b/tools/release.sh index 7e67d4e4c..d41192af9 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -28,7 +28,7 @@ if [ "$1" = "--production" ] ; then CheckVersion "Next version" "$nextversion" RELEASE_BRANCH="candidate-$version" else - version=`grep "__version__" letsencrypt/__init__.py | cut -d\' -f2 | sed s/\.dev0//` + version=`grep "__version__" certbot/__init__.py | cut -d\' -f2 | sed s/\.dev0//` version="$version.dev$(date +%Y%m%d)1" RELEASE_BRANCH="dev-release" echo Releasing developer version "$version"... @@ -45,10 +45,10 @@ export GPG_TTY=$(tty) PORT=${PORT:-1234} # subpackages to be released -SUBPKGS=${SUBPKGS:-"acme letsencrypt-apache letsencrypt-nginx letshelp-letsencrypt"} +SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot"} subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)" -# letsencrypt_compatibility_test is not packaged because: -# - it is not meant to be used by anyone else than Let's Encrypt devs +# certbot_compatibility_test is not packaged because: +# - it is not meant to be used by anyone else than Certbot devs # - it causes problems when running nosetests - the latter tries to # run everything that matches test*, while there are no unittests # there @@ -83,14 +83,14 @@ git checkout "$RELEASE_BRANCH" SetVersion() { ver="$1" - for pkg_dir in $SUBPKGS letsencrypt-compatibility-test + for pkg_dir in $SUBPKGS certbot-compatibility-test do sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py done - sed -i "s/^__version.*/__version__ = '$ver'/" letsencrypt/__init__.py + sed -i "s/^__version.*/__version__ = '$ver'/" certbot/__init__.py # interactive user input - git add -p letsencrypt $SUBPKGS letsencrypt-compatibility-test + git add -p certbot $SUBPKGS certbot-compatibility-test } @@ -117,7 +117,7 @@ done mkdir "dist.$version" -mv dist "dist.$version/letsencrypt" +mv dist "dist.$version/certbot" for pkg_dir in $SUBPKGS do mv $pkg_dir/dist "dist.$version/$pkg_dir/" @@ -140,7 +140,7 @@ pip install -U pip pip install \ --no-cache-dir \ --extra-index-url http://localhost:$PORT \ - letsencrypt $SUBPKGS + certbot $SUBPKGS # stop local PyPI kill $! cd ~- @@ -155,14 +155,14 @@ mkdir ../kgs kgs="../kgs/$version" pip freeze | tee $kgs pip install nose -for module in letsencrypt $subpkgs_modules ; do +for module in certbot $subpkgs_modules ; do echo testing $module nosetests $module done deactivate # pin pip hashes of the things we just built -for pkg in acme letsencrypt letsencrypt-apache ; do +for pkg in acme certbot certbot-apache ; do echo $pkg==$version \\ pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),' done > /tmp/hashes.$$ diff --git a/tools/venv.sh b/tools/venv.sh index 73c3bb110..c9d8fdb9d 100755 --- a/tools/venv.sh +++ b/tools/venv.sh @@ -1,12 +1,19 @@ #!/bin/sh -xe -# Developer virtualenv setup for Let's Encrypt client +# Developer virtualenv setup for Certbot client -export VENV_ARGS="--python python2" +if command -v python2; then + export VENV_ARGS="--python python2" +elif command -v python2.7; then + export VENV_ARGS="--python python2.7" +else + echo "Couldn't find python2 or python2.7 in $PATH" + exit 1 +fi ./tools/_venv_common.sh \ -e acme[dev] \ -e .[dev,docs] \ - -e letsencrypt-apache \ - -e letsencrypt-nginx \ - -e letshelp-letsencrypt \ - -e letsencrypt-compatibility-test + -e certbot-apache \ + -e certbot-nginx \ + -e letshelp-certbot \ + -e certbot-compatibility-test diff --git a/tools/venv3.sh b/tools/venv3.sh index 645ed0d47..35ffac749 100755 --- a/tools/venv3.sh +++ b/tools/venv3.sh @@ -1,5 +1,5 @@ #!/bin/sh -xe -# Developer Python3 virtualenv setup for Let's Encrypt +# Developer Python3 virtualenv setup for Certbot export VENV_NAME="${VENV_NAME:-venv3}" export VENV_ARGS="--python python3" diff --git a/tox.cover.sh b/tox.cover.sh index 8418de9a8..7243c4708 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -9,21 +9,21 @@ # -e makes sure we fail fast and don't submit coveralls submit if [ "xxx$1" = "xxx" ]; then - pkgs="letsencrypt acme letsencrypt_apache letsencrypt_nginx letshelp_letsencrypt" + pkgs="certbot acme certbot_apache certbot_nginx letshelp_certbot" else pkgs="$@" fi cover () { - if [ "$1" = "letsencrypt" ]; then + if [ "$1" = "certbot" ]; then min=98 elif [ "$1" = "acme" ]; then min=100 - elif [ "$1" = "letsencrypt_apache" ]; then + elif [ "$1" = "certbot_apache" ]; then min=100 - elif [ "$1" = "letsencrypt_nginx" ]; then + elif [ "$1" = "certbot_nginx" ]; then min=97 - elif [ "$1" = "letshelp_letsencrypt" ]; then + elif [ "$1" = "letshelp_certbot" ]; then min=100 else echo "Unrecognized package: $1" diff --git a/tox.ini b/tox.ini index 6af9610e3..5c88dfd21 100644 --- a/tox.ini +++ b/tox.ini @@ -3,8 +3,6 @@ # "tox" from this directory. [tox] -# acme and letsencrypt are not yet on pypi, so when Tox invokes -# "install *.zip", it will not find deps skipsdist = true envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint @@ -18,13 +16,13 @@ commands = pip install -e acme[dev] nosetests -v acme pip install -e .[dev] - nosetests -v letsencrypt - pip install -e letsencrypt-apache - nosetests -v letsencrypt_apache - pip install -e letsencrypt-nginx - nosetests -v letsencrypt_nginx - pip install -e letshelp-letsencrypt - nosetests -v letshelp_letsencrypt + nosetests -v certbot + pip install -e certbot-apache + nosetests -v certbot_apache + pip install -e certbot-nginx + nosetests -v certbot_nginx + pip install -e letshelp-certbot + nosetests -v letshelp_certbot setenv = PYTHONPATH = {toxinidir} @@ -56,7 +54,7 @@ commands = [testenv:cover] basepython = python2.7 commands = - pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letshelp-certbot ./tox.cover.sh [testenv:lint] @@ -66,22 +64,21 @@ 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[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot ./pep8.travis.sh - pylint --rcfile=.pylintrc letsencrypt + pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme - pylint --rcfile=.pylintrc letsencrypt-apache/letsencrypt_apache - pylint --rcfile=.pylintrc letsencrypt-nginx/letsencrypt_nginx - pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test - pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt + pylint --rcfile=.pylintrc certbot-apache/certbot_apache + pylint --rcfile=.pylintrc certbot-nginx/certbot_nginx + pylint --rcfile=.pylintrc certbot-compatibility-test/certbot_compatibility_test + pylint --rcfile=.pylintrc letshelp-certbot/letshelp_certbot [testenv:apacheconftest] #basepython = python2.7 -setenv = - LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/apacheconftest/bin/letsencrypt commands = - pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules + pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot + {toxinidir}/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test --debian-modules + [testenv:le_auto] # At the moment, this tests under Python 2.7 only, as only that version is