mirror of
https://github.com/certbot/certbot.git
synced 2026-06-06 07:12:54 -04:00
During review of #6989, we saw that some of our test bash scripts were still used in the Boulder project in particular. It is about `tests/integration/_common.sh` in particular, to expose the `certbot_test` bash function, that is an appropriate way to execute a local version of certbot in test mode: define a custom server, remove several checks, full log and so on. This PR is an attempt to assert this goal: exposing a new `certbot_test` executable for test purpose. More generally, this PR is about giving well suited scripts to quickly make manual tests against certbot without launching the full automated pytest suite. The idea here is to leverage the existing logic in certbot-ci, and expose it as executable scripts. This is done thanks to the `console_scripts` entry of setuptools entrypoint feature, that install scripts in the `PATH`, when `pip install` is invoked, that delegate to specific functions in the installed packages. Two scripts are defined this way: * `certbot_test`: it executes certbot in test mode in a very similar way than the original `certbot_test` in `_common.sh`, by delegating to `certbot_integration_tests.utils.certbot_call:main`. By default this execution will target a pebble directory url started locally. The url, and also http-01/tls-alpn-01 challenge ports can be configured using ad-hoc environment variables. All arguments passed to `certbot_test` are transferred to the underlying certbot command. * `acme_server`: it set up a fully running instance of an ACME server, ready for tests (in particular, all FQDN resolves to localhost in order to target a locally running `certbot_test` command) by delegating to `certbot_integration_tests.utils.acme_server:main`. The choice of the ACME server is given by the first parameter passed to `acme_server`, it can be `pebble`, `boulder-v1` or `boulder-v2`. The command keeps running on foreground, displaying the logs of the ACME server on stdout/stderr. The server is shut down and resources cleaned upon entering CTRL+C. This two commands can be run also through the underlying python modules, that are executable. Finally, a typical workflow on certbot side to run manual tests would be: ``` cd certbot tools/venv.py source venv/bin/activate acme_server pebble & certbot_test certonly --standalone -d test.example.com ``` On boulder side it could be: ``` # Follow certbot dev environment setup instructions, then ... cd boulder docker-compose run --use-aliases -e FAKE_DNS=172.17.0.1 --service-ports boulder ./start.py SERVER=http://localhost:4001/directory certbot_test certonly --standalone -d test.example.com ``` * Configure certbot-ci to expose a certbot_test console script calling certbot in test mode against a local pebble instance * Add a command to start pebble/boulder * Use explicit start * Add execution permission to acme_server * Add a docstring to certbot_test function * Change executable name * Increase sleep to 3600s * Implement a context manager to handle the acme server * Add certbot_test workspace in .gitignore * Add documentation * Remove one function in context, split logic of certbot_test towards capturing non capturing * Use an explicit an properly configured ACMEServer as handler. * Add doc. Put constants.
98 lines
3.8 KiB
Python
Executable file
98 lines
3.8 KiB
Python
Executable file
#!/usr/bin/env python
|
|
"""Module to call certbot in test mode"""
|
|
from __future__ import absolute_import
|
|
from distutils.version import LooseVersion
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
|
|
from certbot_integration_tests.utils import misc
|
|
from certbot_integration_tests.utils.constants import *
|
|
|
|
|
|
def certbot_test(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
|
|
config_dir, workspace, force_renew=True):
|
|
"""
|
|
Invoke the certbot executable available in PATH in a test context for the given args.
|
|
The test context consists in running certbot in debug mode, with various flags suitable
|
|
for tests (eg. no ssl check, customizable ACME challenge ports and config directory ...).
|
|
This command captures stdout and returns it to the caller.
|
|
:param list certbot_args: the arguments to pass to the certbot executable
|
|
:param str directory_url: URL of the ACME directory server to use
|
|
:param int http_01_port: port for the HTTP-01 challenges
|
|
:param int tls_alpn_01_port: port for the TLS-ALPN-01 challenges
|
|
:param str config_dir: certbot configuration directory to use
|
|
:param str workspace: certbot current directory to use
|
|
:param bool force_renew: set False to not force renew existing certificates (default: True)
|
|
:return: stdout as string
|
|
:rtype: str
|
|
"""
|
|
command, env = _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
|
|
config_dir, workspace, force_renew)
|
|
|
|
return subprocess.check_output(command, universal_newlines=True, cwd=workspace, env=env)
|
|
|
|
|
|
def _prepare_args_env(certbot_args, directory_url, http_01_port, tls_alpn_01_port,
|
|
config_dir, workspace, force_renew):
|
|
new_environ = os.environ.copy()
|
|
new_environ['TMPDIR'] = workspace
|
|
|
|
additional_args = []
|
|
if misc.get_certbot_version() >= LooseVersion('0.30.0'):
|
|
additional_args.append('--no-random-sleep-on-renew')
|
|
|
|
if force_renew:
|
|
additional_args.append('--renew-by-default')
|
|
|
|
command = [
|
|
'certbot',
|
|
'--server', directory_url,
|
|
'--no-verify-ssl',
|
|
'--http-01-port', str(http_01_port),
|
|
'--https-port', str(tls_alpn_01_port),
|
|
'--manual-public-ip-logging-ok',
|
|
'--config-dir', config_dir,
|
|
'--work-dir', os.path.join(workspace, 'work'),
|
|
'--logs-dir', os.path.join(workspace, 'logs'),
|
|
'--non-interactive',
|
|
'--no-redirect',
|
|
'--agree-tos',
|
|
'--register-unsafely-without-email',
|
|
'--debug',
|
|
'-vv'
|
|
]
|
|
|
|
command.extend(certbot_args)
|
|
command.extend(additional_args)
|
|
|
|
print('--> Invoke command:\n=====\n{0}\n====='.format(subprocess.list2cmdline(command)))
|
|
|
|
return command, new_environ
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
|
|
# Default config is pebble
|
|
directory_url = os.environ.get('SERVER', PEBBLE_DIRECTORY_URL)
|
|
http_01_port = int(os.environ.get('HTTP_01_PORT', HTTP_01_PORT))
|
|
tls_alpn_01_port = int(os.environ.get('TLS_ALPN_01_PORT', TLS_ALPN_01_PORT))
|
|
|
|
# Execution of certbot in a self-contained workspace
|
|
workspace = os.environ.get('WORKSPACE', os.path.join(os.getcwd(), '.certbot_test_workspace'))
|
|
if not os.path.exists(workspace):
|
|
print('--> Creating a workspace for certbot_test: {0}'.format(workspace))
|
|
os.mkdir(workspace)
|
|
else:
|
|
print('--> Using an existing workspace for certbot_test: {0}'.format(workspace))
|
|
config_dir = os.path.join(workspace, 'conf')
|
|
|
|
# Invoke certbot in test mode, without capturing output so users see directly the outcome.
|
|
command, env = _prepare_args_env(args, directory_url, http_01_port, tls_alpn_01_port,
|
|
config_dir, workspace, True)
|
|
subprocess.check_call(command, universal_newlines=True, cwd=workspace, env=env)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|