From 1146eecac3af17eb5a66ff67364a9fceb4a5d509 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 28 Nov 2019 06:54:58 -0800 Subject: [PATCH] integration: use python3 (#4582) Python 2 is over in 1 month 4 days: https://pythonclock.org/ This rolls forward most of the changes in #4313. The original change was rolled back in #4323 because it broke `docker-compose up`. This change fixes those original issues by (a) making sure `requests` is installed and (b) sourcing a virtualenv containing the `requests` module before running start.py. Other notable changes in this: - Certbot has changed the developer instructions to install specific packages rather than rely on `letsencrypt-auto --os-packages-only`, so we follow suit. - Python3 now has a `bytes` type that is used in some places that used to provide `str`, and all `str` are now Unicode. That means going from `bytes` to `str` and back requires explicit `.decode()` and `.encode()`. - Moved from urllib2 to requests in many places. --- docker-compose.yml | 9 +- start.py | 4 +- test.sh | 4 +- test/boulder-tools/Dockerfile.tmpl | 1 + test/boulder-tools/build.sh | 16 ++- test/{ => boulder-tools}/requirements.txt | 0 test/challtestsrv.py | 7 +- test/chisel.py | 30 ++--- test/chisel2.py | 7 +- test/entrypoint.sh | 6 +- test/helpers.py | 42 +++---- test/integration-test.py | 38 +++--- test/startservers.py | 9 +- test/v1_integration.py | 86 +++++++------- test/v2_integration.py | 136 +++++++++++----------- 15 files changed, 206 insertions(+), 189 deletions(-) rename test/{ => boulder-tools}/requirements.txt (100%) diff --git a/docker-compose.yml b/docker-compose.yml index 5e3343a65..dd2d93a02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,13 +2,16 @@ version: '3' services: boulder: # To minimize fetching this should be the same version used below - image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-10-17 + image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-11-18 environment: FAKE_DNS: 10.77.77.77 PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657 BOULDER_CONFIG_DIR: test/config GO111MODULE: "on" GOFLAGS: "-mod=vendor" + # This is required so Python doesn't throw an error when printing + # non-ASCII to stdout. + PYTHONIOENCODING: "utf-8" volumes: - .:/go/src/github.com/letsencrypt/boulder - ./.gocache:/root/.cache/go-build @@ -55,7 +58,7 @@ services: working_dir: /go/src/github.com/letsencrypt/boulder bhsm: # To minimize fetching this should be the same version used above - image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-10-17 + image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-11-18 environment: PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657 command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so @@ -82,7 +85,7 @@ services: logging: driver: none netaccess: - image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-10-17 + image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2019-11-18 environment: GO111MODULE: "on" GOFLAGS: "-mod=vendor" diff --git a/start.py b/start.py index d9e81acd0..e663cf6e9 100755 --- a/start.py +++ b/start.py @@ -25,8 +25,8 @@ try: startservers.check() sys.exit(1) except KeyboardInterrupt: - print "\nstopping servers." -except OSError, v: + print("\nstopping servers.") +except OSError as v: # Ignore EINTR, which happens when we get SIGTERM or SIGINT (i.e. when # someone hits Ctrl-C after running docker-compose up or start.py. if v.errno != errno.EINTR: diff --git a/test.sh b/test.sh index 69a33a015..6636a2ced 100755 --- a/test.sh +++ b/test.sh @@ -103,9 +103,9 @@ if [[ "$RUN" =~ "integration" ]] ; then args+=("--filter" "${INT_FILTER}") fi - source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv}/bin/activate + source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv3}/bin/activate DIRECTORY=http://boulder:4000/directory \ - python2 test/integration-test.py --chisel --gotest "${args[@]}" + python3 test/integration-test.py --chisel --gotest "${args[@]}" fi # Test that just ./start.py works, which is a proxy for testing that diff --git a/test/boulder-tools/Dockerfile.tmpl b/test/boulder-tools/Dockerfile.tmpl index 384dda0ec..dabf5b2be 100644 --- a/test/boulder-tools/Dockerfile.tmpl +++ b/test/boulder-tools/Dockerfile.tmpl @@ -9,4 +9,5 @@ RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" WORKDIR $GOPATH ADD build.sh /tmp/build.sh +ADD requirements.txt /tmp/requirements.txt RUN /tmp/build.sh diff --git a/test/boulder-tools/build.sh b/test/boulder-tools/build.sh index 7a9f3f623..b40088efe 100755 --- a/test/boulder-tools/build.sh +++ b/test/boulder-tools/build.sh @@ -16,14 +16,21 @@ apt-get install -y --no-install-recommends \ ruby \ ruby-dev \ rsyslog \ - python3-venv \ softhsm \ build-essential \ cmake \ libssl-dev \ libseccomp-dev \ opensc \ - unzip + unzip \ + python3-dev \ + python3-venv \ + gcc \ + libaugeas0 \ + libssl-dev \ + libffi-dev \ + ca-certificates \ + openssl curl -L https://github.com/google/protobuf/releases/download/v3.6.1/protoc-3.6.1-linux-x86_64.zip -o /tmp/protoc.zip unzip /tmp/protoc.zip -d /usr/local/protoc @@ -48,8 +55,9 @@ go get \ git clone https://github.com/certbot/certbot /certbot cd /certbot -./letsencrypt-auto --os-packages-only -./tools/venv.py +./tools/venv3.py +source venv3/bin/activate +pip install -r /tmp/requirements.txt cd - # Install pkcs11-proxy. Checked out commit was master HEAD at time diff --git a/test/requirements.txt b/test/boulder-tools/requirements.txt similarity index 100% rename from test/requirements.txt rename to test/boulder-tools/requirements.txt diff --git a/test/challtestsrv.py b/test/challtestsrv.py index ef49c5610..7b2c535b0 100644 --- a/test/challtestsrv.py +++ b/test/challtestsrv.py @@ -1,5 +1,5 @@ -import urllib2 import json +import requests class ChallTestServer: """ @@ -41,9 +41,10 @@ class ChallTestServer: self._baseURL = url def _postURL(self, url, body): - return urllib2.urlopen( + response = requests.post( url, - data=json.dumps(body)).read() + data=json.dumps(body)) + return response.text def _URL(self, path): urlPath = self._paths.get(path, None) diff --git a/test/chisel.py b/test/chisel.py index 3d0aa132e..93b197a1f 100644 --- a/test/chisel.py +++ b/test/chisel.py @@ -14,7 +14,7 @@ import socket import sys import threading import time -import urllib2 +import requests from cryptography import x509 from cryptography.hazmat.backends import default_backend @@ -73,9 +73,9 @@ def update_email(client, email): email. """ if client is None: - raise NoClientError("update_email requires a valid acme.Client argument") + raise(NoClientError("update_email requires a valid acme.Client argument")) if email is None: - raise EmailRequiredError("update_email requires an email argument") + raise(EmailRequiredError("update_email requires an email argument")) if not email.startswith("mailto:"): email = "mailto:"+ email acct = client.account @@ -86,7 +86,7 @@ def get_chall(authz, typ): for chall_body in authz.body.challenges: if isinstance(chall_body.chall, typ): return chall_body - raise Exception("No %s challenge found" % typ) + raise(Exception("No %s challenge found" % typ)) class ValidationError(Exception): """An error that occurs during challenge validation.""" @@ -110,9 +110,9 @@ def issue(client, authzs, cert_output=None): csr = OpenSSL.crypto.X509Req() csr.add_extensions([ OpenSSL.crypto.X509Extension( - 'subjectAltName', + 'subjectAltName'.encode(), critical=False, - value=', '.join('DNS:' + d for d in domains).encode() + value=(', '.join('DNS:' + d for d in domains)).encode() ), ]) csr.set_pubkey(pkey) @@ -126,19 +126,21 @@ def issue(client, authzs, cert_output=None): # If we get a PollError, pick the first failed authz and turn it into a more # useful ValidationError that contains details we can look for in tests. for authz in error.updated: - updated_authz = json.loads(urllib2.urlopen(authz.uri).read()) + r = requests.get(authz.uri) + r.raise_for_status() + updated_authz = r.json() domain = authz.body.identifier.value, for c in updated_authz['challenges']: if 'error' in c: err = c['error'] - raise ValidationError(domain, err['type'], err['detail']) + raise(ValidationError(domain, err['type'], err['detail'])) # If none of the authz's had an error, just re-raise. raise if cert_output is not None: pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_resource.body) with open(cert_output, 'w') as f: - f.write(pem) + f.write(pem.decode()) return cert_resource def http_01_answer(client, chall_body): @@ -223,7 +225,7 @@ def auth_and_issue(domains, chall_type="dns-01", email=None, cert_output=None, c elif chall_type == "tls-alpn-01": cleanup = do_tlsalpn_challenges(client, authzs) else: - raise Exception("invalid challenge type %s" % chall_type) + raise(Exception("invalid challenge type %s" % chall_type)) try: cert_resource = issue(client, authzs, cert_output) @@ -250,15 +252,15 @@ def expect_problem(problem_type, func): else: raise if not ok: - raise Exception('Expected %s, got no error' % problem_type) + raise(Exception('Expected %s, got no error' % problem_type)) if __name__ == "__main__": domains = sys.argv[1:] if len(domains) == 0: - print __doc__ + print(__doc__) sys.exit(0) try: auth_and_issue(domains) - except messages.Error, e: - print e + except messages.Error as e: + print(e) sys.exit(1) diff --git a/test/chisel2.py b/test/chisel2.py index 39ca19bea..f43ea93e1 100644 --- a/test/chisel2.py +++ b/test/chisel2.py @@ -15,7 +15,6 @@ import sys import signal import threading import time -import urllib2 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa @@ -189,10 +188,10 @@ if __name__ == "__main__": signal.signal(signal.SIGINT, signal.SIG_DFL) domains = sys.argv[1:] if len(domains) == 0: - print __doc__ + print(__doc__) sys.exit(0) try: auth_and_issue(domains) - except messages.Error, e: - print e + except messages.Error as e: + print(e) sys.exit(1) diff --git a/test/entrypoint.sh b/test/entrypoint.sh index 07e49e155..33557fa83 100755 --- a/test/entrypoint.sh +++ b/test/entrypoint.sh @@ -51,7 +51,11 @@ if [ -n "${PKCS11_PROXY_SOCKET:-}" ]; then fi if [[ $# -eq 0 ]]; then - exec ./start.py + # the activate script touches PS1, which is undefined, so we have to relax + # the "fail on undefined" setting here. + set +u + source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv3}/bin/activate + exec python3 ./start.py fi exec $@ diff --git a/test/helpers.py b/test/helpers.py index 2873a9935..d9c216725 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -1,11 +1,11 @@ -#!/usr/bin/env python2.7 import base64 import os -import urllib2 +import urllib import time import re import random import json +import requests import socket import tempfile import shutil @@ -38,20 +38,20 @@ def random_domain(): return "rand.%x.xyz" % random.randrange(2**32) def run(cmd, **kwargs): - return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs) + return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs).decode() def fetch_ocsp(request_bytes, url): """Fetch an OCSP response using POST, GET, and GET with URL encoding. Returns a tuple of the responses. """ - ocsp_req_b64 = base64.b64encode(request_bytes) + ocsp_req_b64 = base64.b64encode(request_bytes).decode() # Make the OCSP request three different ways: by POST, by GET, and by GET with # URL-encoded parameters. All three should have an identical response. - get_response = urllib2.urlopen("%s/%s" % (url, ocsp_req_b64)).read() - get_encoded_response = urllib2.urlopen("%s/%s" % (url, urllib2.quote(ocsp_req_b64, safe = ""))).read() - post_response = urllib2.urlopen("%s/" % (url), request_bytes).read() + get_response = requests.get("%s/%s" % (url, ocsp_req_b64)).content + get_encoded_response = requests.get("%s/%s" % (url, urllib.parse.quote(ocsp_req_b64, safe = ""))).content + post_response = requests.post("%s/" % (url), data=request_bytes).content return (post_response, get_response, get_encoded_response) @@ -61,13 +61,13 @@ def make_ocsp_req(cert_file, issuer_file): # First generate the OCSP request in DER form run("openssl ocsp -no_nonce -issuer %s -cert %s -reqout %s" % ( issuer_file, cert_file, ocsp_req_file)) - with open(ocsp_req_file) as f: + with open(ocsp_req_file, mode='rb') as f: ocsp_req = f.read() return ocsp_req def ocsp_verify(cert_file, issuer_file, ocsp_response): ocsp_resp_file = os.path.join(tempdir, "ocsp.resp") - with open(ocsp_resp_file, "w") as f: + with open(ocsp_resp_file, "wb") as f: f.write(ocsp_response) output = run("openssl ocsp -no_nonce -issuer %s -cert %s \ -verify_other %s -CAfile test/test-root.pem \ @@ -76,8 +76,8 @@ def ocsp_verify(cert_file, issuer_file, ocsp_response): # also look for the string "Response Verify Failure" verify_failure = "Response Verify Failure" if re.search(verify_failure, output): - print output - raise Exception("OCSP verify failure") + print(output) + raise(Exception("OCSP verify failure")) return output def verify_ocsp(cert_file, issuer_file, url, status): @@ -87,28 +87,28 @@ def verify_ocsp(cert_file, issuer_file, url, status): # Verify all responses are the same for resp in responses: if resp != responses[0]: - raise Exception("OCSP responses differed: %s vs %s" %( - base64.b64encode(responses[0]), base64.b64encode(resp))) + raise(Exception("OCSP responses differed: %s vs %s" %( + base64.b64encode(responses[0]), base64.b64encode(resp)))) # Check response is for the correct certificate and is correct # status resp = responses[0] verify_output = ocsp_verify(cert_file, issuer_file, resp) if not re.search("%s: %s" % (cert_file, status), verify_output): - print verify_output - raise Exception("OCSP response wasn't '%s'" % status) + print(verify_output) + raise(Exception("OCSP response wasn't '%s'" % status)) def reset_akamai_purges(): - urllib2.urlopen("http://localhost:6789/debug/reset-purges", "{}") + requests.post("http://localhost:6789/debug/reset-purges", data="{}") def verify_akamai_purge(): deadline = time.time() + 0.25 while True: time.sleep(0.05) if time.time() > deadline: - raise Exception("Timed out waiting for Akamai purge") - response = urllib2.urlopen("http://localhost:6789/debug/get-purges") - purgeData = json.load(response) + raise(Exception("Timed out waiting for Akamai purge")) + response = requests.get("http://localhost:6789/debug/get-purges") + purgeData = response.json() if len(purgeData["V3"]) is not 1: continue break @@ -150,7 +150,7 @@ def waitport(port, prog, perTickCheck=None): return True except socket.error as e: if e.errno == errno.ECONNREFUSED: - print "Waiting for debug port %d (%s)" % (port, prog) + print("Waiting for debug port %d (%s)" % (port, prog)) else: raise - raise Exception("timed out waiting for debug port %d (%s)" % (port, prog)) + raise(Exception("timed out waiting for debug port %d (%s)" % (port, prog))) diff --git a/test/integration-test.py b/test/integration-test.py index f517636b9..263470f8a 100644 --- a/test/integration-test.py +++ b/test/integration-test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ This file contains basic infrastructure for running the integration test cases. @@ -60,13 +60,13 @@ def run_expired_authz_purger(): tool = "expired-authz-purger2" out = get_future_output("./bin/expired-authz-purger2 --single-run --config cmd/expired-authz-purger2/config.json", target_time) if 'via FAKECLOCK' not in out: - raise Exception("%s was not built with `integration` build tag" % (tool)) + raise(Exception("%s was not built with `integration` build tag" % (tool))) if num is None: return expected_output = 'deleted %d expired authorizations' % (num) if expected_output not in out: - raise Exception("%s did not print '%s'. Output:\n%s" % ( - tool, expected_output, out)) + raise(Exception("%s did not print '%s'. Output:\n%s" % ( + tool, expected_output, out))) now = datetime.datetime.utcnow() @@ -112,7 +112,7 @@ def run_janitor(): def get_stat_line(port, stat): url = "http://localhost:%d/metrics" % port response = requests.get(url) - for l in response.content.split("\n"): + for l in response.text.split("\n"): if l.strip().startswith(stat): return l return None @@ -120,7 +120,7 @@ def run_janitor(): def stat_value(line): parts = line.split(" ") if len(parts) != 2: - raise Exception("stat line {0} was missing required parts".format(line)) + raise(Exception("stat line {0} was missing required parts".format(line))) return parts[1] # Wait for the janitor to finish its work. The easiest way to tell this @@ -129,7 +129,7 @@ def run_janitor(): attempts = 0 while True: if attempts > 5: - raise Exception("timed out waiting for janitor workbatch counts to stabilize") + raise(Exception("timed out waiting for janitor workbatch counts to stabilize")) certStatusWorkBatch = get_stat_line(8014, statline("workbatch", "certificateStatus")) certsWorkBatch = get_stat_line(8014, statline("workbatch", "certificates")) @@ -166,7 +166,7 @@ def run_janitor(): for l in [certStatusDeletes, certsDeletes, certsPerNameDeletes, ordersDeletes]: if stat_value(l) == "0": - raise Exception("Expected a non-zero number of deletes to be performed. Found {0}".format(l)) + raise(Exception("Expected a non-zero number of deletes to be performed. Found {0}".format(l))) # Check that all error stats are empty errorStats = [ @@ -178,7 +178,7 @@ def run_janitor(): for eStat in errorStats: actual = get_stat_line(8014, eStat) if actual is not None: - raise Exception("Expected to find no error stat lines but found {0}\n".format(eStat)) + raise(Exception("Expected to find no error stat lines but found {0}\n".format(eStat))) # Terminate the janitor p.terminate() @@ -221,9 +221,9 @@ def test_stats(): def expect_stat(port, stat): url = "http://localhost:%d/metrics" % port response = requests.get(url) - if not stat in response.content: + if not stat in response.text: print(response.content) - raise Exception("%s not present in %s" % (stat, url)) + raise(Exception("%s not present in %s" % (stat, url))) expect_stat(8000, "\nresponse_time_count{") expect_stat(8000, "\ngo_goroutines ") expect_stat(8000, '\ngrpc_client_handling_seconds_count{grpc_method="NewRegistration",grpc_service="ra.RegistrationAuthority",grpc_type="unary"} ') @@ -251,27 +251,27 @@ def main(): test_case_filter="", skip_setup=False) args = parser.parse_args() - if not (args.run_certbot or args.run_chisel or args.run_loadtest or args.custom is not None): - raise Exception("must run at least one of the letsencrypt or chisel tests with --certbot, --chisel, or --custom") + if not (args.run_certbot or args.run_chisel or args.custom is not None): + raise(Exception("must run at least one of the letsencrypt or chisel tests with --certbot, --chisel, or --custom")) if not args.test_case_filter: now = datetime.datetime.utcnow() six_months_ago = now+datetime.timedelta(days=-30*6) if not startservers.start(race_detection=True, fakeclock=fakeclock(six_months_ago)): - raise Exception("startservers failed (mocking six months ago)") + raise(Exception("startservers failed (mocking six months ago)")) v1_integration.caa_client = caa_client = chisel.make_client() setup_six_months_ago() startservers.stop() twenty_days_ago = now+datetime.timedelta(days=-20) if not startservers.start(race_detection=True, fakeclock=fakeclock(twenty_days_ago)): - raise Exception("startservers failed (mocking twenty days ago)") + raise(Exception("startservers failed (mocking twenty days ago)")) setup_twenty_days_ago() startservers.stop() if not startservers.start(race_detection=True, fakeclock=None): - raise Exception("startservers failed") + raise(Exception("startservers failed")) if args.run_chisel: run_chisel(args.test_case_filter) @@ -304,7 +304,7 @@ def main(): run_loadtest() if not startservers.check(): - raise Exception("startservers.check failed") + raise(Exception("startservers.check failed")) check_slow_queries() @@ -390,7 +390,7 @@ def check_balance(): for address in addresses: metrics = requests.get("http://%s/metrics" % address) if not "grpc_server_handled_total" in metrics.text: - raise Exception("no gRPC traffic processed by %s; load balancing problem?" + raise(Exception("no gRPC traffic processed by %s; load balancing problem?") % address) def run_cert_checker(): @@ -400,7 +400,7 @@ if __name__ == "__main__": try: main() except subprocess.CalledProcessError as e: - raise Exception("%s. Output:\n%s" % (e, e.output)) + raise(Exception("%s. Output:\n%s" % (e, e.output))) @atexit.register def stop(): diff --git a/test/startservers.py b/test/startservers.py index d40d8d0f2..bb53bdf27 100644 --- a/test/startservers.py +++ b/test/startservers.py @@ -1,5 +1,4 @@ import atexit -import BaseHTTPServer import os import shutil import signal @@ -98,7 +97,7 @@ def start(race_detection, fakeclock): print(e) return False - print "All servers running. Hit ^C to kill." + print("All servers running. Hit ^C to kill.") return True def check(): @@ -116,9 +115,9 @@ def check(): else: busted.append(p) if busted: - print "\n\nThese processes exited early (check above for their output):" + print("\n\nThese processes exited early (check above for their output):") for p in busted: - print "\t'%s' with pid %d exited %d" % (p.cmd, p.pid, p.returncode) + print("\t'%s' with pid %d exited %d" % (p.cmd, p.pid, p.returncode)) processes = stillok return not busted @@ -129,7 +128,7 @@ def startChallSrv(): """ global challSrvProcess if challSrvProcess is not None: - raise Exception("startChallSrv called more than once") + raise(Exception("startChallSrv called more than once")) # NOTE(@cpu): We specify explicit bind addresses for -https01 and # --tlsalpn01 here to allow HTTPS HTTP-01 responses on 5001 for on interface diff --git a/test/v1_integration.py b/test/v1_integration.py index cc6cf2ffb..7e1337504 100644 --- a/test/v1_integration.py +++ b/test/v1_integration.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- import datetime import json @@ -7,7 +6,6 @@ import random import re import requests import time -import urllib2 import startservers @@ -40,7 +38,7 @@ def rand_http_chall(client): for c in authz.body.challenges: if isinstance(c.chall, challenges.HTTP01): return d, c.chall - raise Exception("No HTTP-01 challenge found for random domain authz") + raise(Exception("No HTTP-01 challenge found for random domain authz")) def test_http_challenge_loop_redirect(): client = chisel.make_client() @@ -158,7 +156,7 @@ def test_http_challenge_http_redirect(): # There should have been at least two GET requests made to the # challtestsrv. There may have been more if remote VAs were configured. if len(history) < 2: - raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history))) + raise(Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))) initialRequests = [] redirectedRequests = [] @@ -166,7 +164,7 @@ def test_http_challenge_http_redirect(): for request in history: # All requests should have been over HTTP if request['HTTPS'] is True: - raise Exception("Expected all requests to be HTTP") + raise(Exception("Expected all requests to be HTTP")) # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) @@ -175,15 +173,15 @@ def test_http_challenge_http_redirect(): elif request['URL'] == redirectPath: redirectedRequests.append(request) else: - raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) + raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: - raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) + raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: - raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) + raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) def test_http_challenge_https_redirect(): client = chisel.make_client() @@ -220,7 +218,7 @@ def test_http_challenge_https_redirect(): # There should have been at least two GET requests made to the challtestsrv by the VA if len(history) < 2: - raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history))) + raise(Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))) initialRequests = [] redirectedRequests = [] @@ -234,26 +232,26 @@ def test_http_challenge_https_redirect(): elif request['URL'] == redirectPath: redirectedRequests.append(request) else: - raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) + raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: - raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) + raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) # All initial requests should have been over HTTP for r in initialRequests: if r['HTTPS'] is True: - raise Exception("Expected all initial requests to be HTTP") + raise(Exception("Expected all initial requests to be HTTP")) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: - raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) + raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) # All the redirected requests should have been over HTTPS with the correct # SNI value for r in redirectedRequests: if r['HTTPS'] is False: - raise Exception("Expected all redirected requests to be HTTPS") + raise(Exception("Expected all redirected requests to be HTTPS")) elif r['ServerName'] != d: - raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName'])) + raise(Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))) class SlowHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): @@ -315,7 +313,7 @@ def test_http_challenge_timeout(): # present the timeout is 20s so adding 2s of padding = 22s) expectedDuration = 22 if delta.total_seconds() == 0 or delta.total_seconds() > expectedDuration: - raise Exception("expected timeout to occur in under {0} seconds. Took {1}".format(expectedDuration, delta.total_seconds())) + raise(Exception("expected timeout to occur in under {0} seconds. Took {1}".format(expectedDuration, delta.total_seconds()))) def test_tls_alpn_challenge(): # Pick two random domains @@ -339,7 +337,7 @@ def test_issuer(): of length exactly 1. """ certr, authzs = auth_and_issue([random_domain()]) - cert = urllib2.urlopen(certr.uri).read() + cert = requests.get(certr.uri).content # In the future the chain URI will use HTTPS so include the root certificate # for the WFE's PKI. Note: We use the requests library here so we honor the # REQUESTS_CA_BUNDLE passed by test.sh. @@ -382,7 +380,7 @@ def test_ct_submission(): def submissions(group): count = 0 for log in group: - count += int(urllib2.urlopen(log + "?hostnames=%s" % hostname).read()) + count += int(requests.get(log + "?hostnames=%s" % hostname).text) return count auth_and_issue([hostname]) @@ -392,29 +390,29 @@ def test_ct_submission(): for i in range(len(log_groups)): if got[i] < expected[i]: - raise Exception("For log group %d, got %d submissions, expected %d." % - (i, got[i], expected[i])) + raise(Exception("For log group %d, got %d submissions, expected %d." % + (i, got[i], expected[i]))) def test_expiration_mailer(): email_addr = "integration.%x@letsencrypt.org" % random.randrange(2**16) cert, _ = auth_and_issue([random_domain()], email=email_addr) # Check that the expiration mailer sends a reminder - expiry = datetime.datetime.strptime(cert.body.get_notAfter(), '%Y%m%d%H%M%SZ') + expiry = datetime.datetime.strptime(cert.body.get_notAfter().decode(), '%Y%m%d%H%M%SZ') no_reminder = expiry + datetime.timedelta(days=-31) first_reminder = expiry + datetime.timedelta(days=-13) last_reminder = expiry + datetime.timedelta(days=-2) - urllib2.urlopen("http://localhost:9381/clear", data='') - print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % - config_dir, no_reminder) - print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % - config_dir, first_reminder) - print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % - config_dir, last_reminder) - resp = urllib2.urlopen("http://localhost:9381/count?to=%s" % email_addr) - mailcount = int(resp.read()) + requests.post("http://localhost:9381/clear", data='') + print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % + config_dir, no_reminder)) + print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % + config_dir, first_reminder)) + print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % + config_dir, last_reminder)) + resp = requests.get("http://localhost:9381/count?to=%s" % email_addr) + mailcount = int(resp.text) if mailcount != 2: - raise Exception("\nExpiry mailer failed: expected 2 emails, got %d" % mailcount) + raise(Exception("\nExpiry mailer failed: expected 2 emails, got %d" % mailcount)) def test_revoke_by_account(): client = chisel.make_client() @@ -450,13 +448,13 @@ def test_recheck_caa(): recheck CAA and reject the request. """ if len(caa_recheck_authzs) == 0: - raise Exception("CAA authzs not prepared for test_caa") + raise(Exception("CAA authzs not prepared for test_caa")) domains = [] for a in caa_recheck_authzs: response = requests.get(a.uri) if response.status_code != 200: - raise Exception("Unexpected response for CAA authz: ", - response.status_code) + raise(Exception("Unexpected response for CAA authz: ", + response.status_code)) domain = a.body.identifier.value domains.append(domain) challSrv.add_caa_issue(domain, ";") @@ -524,11 +522,11 @@ def test_account_update(): result = chisel.update_email(client, email=email) # We expect one contact in the result if len(result.body.contact) != 1: - raise Exception("\nUpdate account failed: expected one contact in result, got 0") + raise(Exception("\nUpdate account failed: expected one contact in result, got 0")) # We expect it to be the email we just updated to actual = result.body.contact[0] if actual != "mailto:"+email: - raise Exception("\nUpdate account failed: expected contact %s, got %s" % (email, actual)) + raise(Exception("\nUpdate account failed: expected contact %s, got %s" % (email, actual))) def test_renewal_exemption(): """ @@ -584,13 +582,13 @@ def test_admin_revoker_cert(): def test_sct_embedding(): certr, authzs = auth_and_issue([random_domain()]) - certBytes = urllib2.urlopen(certr.uri).read() + certBytes = requests.get(certr.uri).content cert = x509.load_der_x509_certificate(certBytes, default_backend()) # make sure there is no poison extension try: cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) - raise Exception("certificate contains CT poison extension") + raise(Exception("certificate contains CT poison extension")) except x509.ExtensionNotFound: # do nothing pass @@ -599,18 +597,18 @@ def test_sct_embedding(): try: sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) except x509.ExtensionNotFound: - raise Exception("certificate doesn't contain SCT list extension") + raise(Exception("certificate doesn't contain SCT list extension")) if len(sctList.value) != 2: - raise Exception("SCT list contains wrong number of SCTs") + raise(Exception("SCT list contains wrong number of SCTs")) for sct in sctList.value: if sct.version != x509.certificate_transparency.Version.v1: - raise Exception("SCT contains wrong version") + raise(Exception("SCT contains wrong version")) if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: - raise Exception("SCT contains wrong entry type") + raise(Exception("SCT contains wrong entry type")) delta = sct.timestamp - datetime.datetime.now() if abs(delta) > datetime.timedelta(hours=1): - raise Exception("Delta between SCT timestamp and now was too great " - "%s vs %s (%s)" % (sct.timestamp, datetime.datetime.now(), delta)) + raise(Exception("Delta between SCT timestamp and now was too great " + "%s vs %s (%s)" % (sct.timestamp, datetime.datetime.now(), delta))) def test_auth_deactivation(): client = chisel.make_client(None) diff --git a/test/v2_integration.py b/test/v2_integration.py index b477086ae..e72bdb3c1 100644 --- a/test/v2_integration.py +++ b/test/v2_integration.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- """ Integration test cases for ACMEv2 as implemented by boulder-wfe2. @@ -67,7 +66,7 @@ def rand_http_chall(client): for c in a.body.challenges: if isinstance(c.chall, challenges.HTTP01): return d, c.chall - raise Exception("No HTTP-01 challenge found for random domain authz") + raise(Exception("No HTTP-01 challenge found for random domain authz")) def check_challenge_dns_err(chalType): """ @@ -104,20 +103,20 @@ def check_challenge_dns_err(chalType): elif chalType == "tls-alpn-01": c = chisel2.get_chall(authzr, challenges.TLSALPN01) else: - raise Exception("Invalid challenge type requested: {0}".format(challType)) + raise(Exception("Invalid challenge type requested: {0}".format(challType))) # The failed challenge's error should match expected error = c.error if error is None or error.typ != "urn:ietf:params:acme:error:{0}".format(expectedProbType): - raise Exception("Expected {0} prob, got {1}".format(expectedProbType, error.typ)) + raise(Exception("Expected {0} prob, got {1}".format(expectedProbType, error.typ))) if not expectedProbRegex.match(error.detail): - raise Exception("Prob detail did not match expectedProbRegex, got \"{0}\"".format(error.detail)) + raise(Exception("Prob detail did not match expectedProbRegex, got \"{0}\"".format(error.detail))) finally: challSrv.remove_servfail_response(d) # If there was no exception that means something went wrong. The test should fail. if failed is False: - raise Exception("No problem generated issuing for broken DNS identifier") + raise(Exception("No problem generated issuing for broken DNS identifier")) def test_http_challenge_dns_err(): """ @@ -171,9 +170,9 @@ def test_http_challenge_broken_redirect(): c = chisel2.get_chall(authzr, challenges.HTTP01) error = c.error if error is None or error.typ != "urn:ietf:params:acme:error:connection": - raise Exception("Expected connection prob, got %s" % (error.__str__())) + raise(Exception("Expected connection prob, got %s" % (error.__str__()))) if error.detail != expectedError: - raise Exception("Expected prob detail %s, got %s" % (expectedError, error.detail)) + raise(Exception("Expected prob detail %s, got %s" % (expectedError, error.detail))) challSrv.remove_http_redirect(challengePath) @@ -317,7 +316,7 @@ def test_http_challenge_http_redirect(): # There should have been at least two GET requests made to the # challtestsrv. There may have been more if remote VAs were configured. if len(history) < 2: - raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history))) + raise(Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))) initialRequests = [] redirectedRequests = [] @@ -325,7 +324,7 @@ def test_http_challenge_http_redirect(): for request in history: # All requests should have been over HTTP if request['HTTPS'] is True: - raise Exception("Expected all requests to be HTTP") + raise(Exception("Expected all requests to be HTTP")) # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) @@ -334,15 +333,15 @@ def test_http_challenge_http_redirect(): elif request['URL'] == redirectPath: redirectedRequests.append(request) else: - raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) + raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: - raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) + raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: - raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) + raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) def test_http_challenge_https_redirect(): client = chisel2.make_client() @@ -377,7 +376,7 @@ def test_http_challenge_https_redirect(): error = chall.error if error: problems.append(error.__str__()) - raise Exception("validation problem: %s" % "; ".join(problems)) + raise(Exception("validation problem: %s" % "; ".join(problems))) challSrv.remove_http_redirect(challengePath) challSrv.remove_a_record(d) @@ -387,7 +386,7 @@ def test_http_challenge_https_redirect(): # There should have been at least two GET requests made to the challtestsrv by the VA if len(history) < 2: - raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history))) + raise(Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))) initialRequests = [] redirectedRequests = [] @@ -401,30 +400,30 @@ def test_http_challenge_https_redirect(): elif request['URL'] == redirectPath: redirectedRequests.append(request) else: - raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) + raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: - raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) + raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) # All initial requests should have been over HTTP for r in initialRequests: if r['HTTPS'] is True: - raise Exception("Expected all initial requests to be HTTP, got %s" % r) + raise(Exception("Expected all initial requests to be HTTP, got %s" % r)) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: - raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) + raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) # All the redirected requests should have been over HTTPS with the correct # SNI value for r in redirectedRequests: if r['HTTPS'] is False: - raise Exception("Expected all redirected requests to be HTTPS") + raise(Exception("Expected all redirected requests to be HTTPS")) # TODO(@cpu): The following ServerName test will fail with config-next # until https://github.com/letsencrypt/boulder/issues/3969 is fixed. if CONFIG_NEXT: return elif r['ServerName'] != d: - raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName'])) + raise(Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))) def test_tls_alpn_challenge(): # Pick two random domains @@ -453,8 +452,8 @@ def test_overlapping_wildcard(): authzs = order.authorizations if len(authzs) != 2: - raise Exception("order for %s had %d authorizations, expected 2" % - (domains, len(authzs))) + raise(Exception("order for %s had %d authorizations, expected 2" % + (domains, len(authzs)))) cleanup = chisel2.do_dns_challenges(client, authzs) try: @@ -519,8 +518,8 @@ def test_wildcard_authz_reuse(): # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): - raise Exception("order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" % - ((domains), str(authz.body.status))) + raise(Exception("order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" % + ((domains), str(authz.body.status)))) def test_bad_overlap_wildcard(): chisel2.expect_problem("urn:ietf:params:acme:error:malformed", @@ -569,20 +568,20 @@ def test_order_reuse_failed_authz(): # If the poll ended and an authz's status isn't invalid then we reached the # deadline, fail the test if not authzFailed: - raise Exception("timed out waiting for order %s to become invalid" % firstOrderURI) + raise(Exception("timed out waiting for order %s to become invalid" % firstOrderURI)) # Make another order with the same domains order = client.new_order(csr_pem) # It should not be the same order as before if order.uri == firstOrderURI: - raise Exception("new-order for %s returned a , now-invalid, order" % domains) + raise(Exception("new-order for %s returned a , now-invalid, order" % domains)) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): - raise Exception("order for %s included a non-pending authorization (status: %s) from a previous order" % - ((domains), str(authz.body.status))) + raise(Exception("order for %s included a non-pending authorization (status: %s) from a previous order" % + ((domains), str(authz.body.status)))) # We expect the new order can be fulfilled cleanup = chisel2.do_http_challenges(client, order.authorizations) @@ -683,12 +682,13 @@ def test_revoke_by_privkey(): def test_sct_embedding(): order = chisel2.auth_and_issue([random_domain()]) - cert = x509.load_pem_x509_certificate(str(order.fullchain_pem), default_backend()) + print(order.fullchain_pem.encode()) + cert = x509.load_pem_x509_certificate(order.fullchain_pem.encode(), default_backend()) # make sure there is no poison extension try: cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) - raise Exception("certificate contains CT poison extension") + raise(Exception("certificate contains CT poison extension")) except x509.ExtensionNotFound: # do nothing pass @@ -697,14 +697,14 @@ def test_sct_embedding(): try: sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) except x509.ExtensionNotFound: - raise Exception("certificate doesn't contain SCT list extension") + raise(Exception("certificate doesn't contain SCT list extension")) if len(sctList.value) != 2: - raise Exception("SCT list contains wrong number of SCTs") + raise(Exception("SCT list contains wrong number of SCTs")) for sct in sctList.value: if sct.version != x509.certificate_transparency.Version.v1: - raise Exception("SCT contains wrong version") + raise(Exception("SCT contains wrong version")) if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: - raise Exception("SCT contains wrong entry type") + raise(Exception("SCT contains wrong entry type")) def test_only_return_existing_reg(): client = chisel2.uninitialized_client() @@ -723,7 +723,7 @@ def test_only_return_existing_reg(): }) resp = client.net.post(client.directory['newAccount'], acct, acme_version=2) if resp.status_code != 200: - raise Exception("incorrect response returned for onlyReturnExisting") + raise(Exception("incorrect response returned for onlyReturnExisting")) other_client = chisel2.uninitialized_client() newAcct = extendedAcct({ @@ -770,7 +770,7 @@ def BouncerHTTPRequestHandler(redirect, guestlist): self.log_message("BouncerHandler UA {0} has no requests on the Guestlist. Sending request to the curb".format(ua)) self.send_response(200) self.end_headers() - self.wfile.write(b'(• ◡ •) <( VIPs only! )') + self.wfile.write(u'(• ◡ •) <( VIPs only! )') BouncerHandler.guestlist = guestlist BouncerHandler.redirect = redirect @@ -810,7 +810,7 @@ def multiva_setup(client, guestlist, domain=None): if isinstance(c.chall, challenges.HTTP01): chall = c.chall if chall is None: - raise Exception("No HTTP-01 challenge found for random domain authz") + raise(Exception("No HTTP-01 challenge found for random domain authz")) token = chall.encode("token") @@ -894,16 +894,16 @@ def test_http_multiva_threshold_fail(): # test needs to unpack an `acme_errors.ValidationError` on its own. It # might be possible to clean this up in the future. if len(e.failed_authzrs) != 1: - raise Exception("expected one failed authz, found {0}".format(len(e.failed_authzrs))) + raise(Exception("expected one failed authz, found {0}".format(len(e.failed_authzrs)))) challs = e.failed_authzrs[0].body.challenges httpChall = None for chall_body in challs: if isinstance(chall_body.chall, challenges.HTTP01): httpChall = chall_body if httpChall is None: - raise Exception("no HTTP-01 challenge in failed authz") + raise(Exception("no HTTP-01 challenge in failed authz")) if httpChall.error.typ != "urn:ietf:params:acme:error:unauthorized": - raise Exception("expected unauthorized prob, found {0}".format(httpChall.error.typ)) + raise(Exception("expected unauthorized prob, found {0}".format(httpChall.error.typ))) finally: cleanup() @@ -945,7 +945,7 @@ def test_http_multiva_threshold_fail_account_disabled(): # Find the numeric ID it was assigned by the ACME server acctURI = client.net.account.uri if len(acctURI.split("/")) < 1: - raise Exception("invalid account URI for newly registered account: {0}".format(acctURI)) + raise(Exception("invalid account URI for newly registered account: {0}".format(acctURI))) acctID = acctURI.split("/")[-1:][0] def run_query(query): @@ -1043,7 +1043,7 @@ def wait_for_tcp_server(addr, port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect((addr, port)) - sock.sendall("\n") + sock.sendall(b"\n") return except socket.error: time.sleep(0.5) @@ -1085,9 +1085,9 @@ def test_http2_http01_challenge(): c = chisel2.get_chall(authzr, challenges.HTTP01) error = c.error if error is None or error.typ != "urn:ietf:params:acme:error:connection": - raise Exception("Expected connection prob, got %s" % (error.__str__())) + raise(Exception("Expected connection prob, got %s" % (error.__str__()))) if not error.detail.endswith(expectedError): - raise Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail)) + raise(Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail))) finally: server.shutdown() server.server_close() @@ -1121,26 +1121,26 @@ def test_new_order_policy_errs(): if e.detail != 'Error creating new order :: Cannot issue for "between-addr.in-addr.arpa": The ACME server refuses to issue a certificate for this domain name, because it is forbidden by policy (and 1 more problems. Refer to sub-problems for more information.)': raise(Exception('Order problem detail did not match expected')) if not ok: - raise Exception('Expected problem, got no error') + raise(Exception('Expected problem, got no error')) def test_long_san_no_cn(): try: chisel2.auth_and_issue([''.join(random.choice(string.ascii_uppercase) for x in range(61)) + ".com"]) # if we get to this raise the auth_and_issue call didn't fail, so fail the test - raise Exception("Issuance didn't fail when the only SAN in a certificate was longer than the max CN length") + raise(Exception("Issuance didn't fail when the only SAN in a certificate was longer than the max CN length")) except messages.Error as e: if e.typ != "urn:ietf:params:acme:error:badCSR": - raise Exception('Expected malformed type problem, got {0}'.format(e.typ)) + raise(Exception('Expected malformed type problem, got {0}'.format(e.typ))) if e.detail != 'Error finalizing order :: issuing precertificate: CSR doesn\'t contain a SAN short enough to fit in CN': - raise Exception('Problem detail did not match expected') + raise(Exception('Problem detail did not match expected')) def test_delete_unused_challenges(): order = chisel2.auth_and_issue([random_domain()], chall_type="dns-01") a = order.authorizations[0] if len(a.body.challenges) != 1: - raise Exception("too many challenges (%d) left after validation" % len(a.body.challenges)) + raise(Exception("too many challenges (%d) left after validation" % len(a.body.challenges))) if not isinstance(a.body.challenges[0].chall, challenges.DNS01): - raise Exception("wrong challenge type left after validation") + raise(Exception("wrong challenge type left after validation")) # intentionally fail a challenge client = chisel2.make_client() @@ -1154,10 +1154,10 @@ def test_delete_unused_challenges(): break time.sleep(1) if len(a.body.challenges) != 1: - raise Exception("too many challenges (%d) left after failed validation" % - len(a.body.challenges)) + raise(Exception("too many challenges (%d) left after failed validation" % + len(a.body.challenges))) if not isinstance(a.body.challenges[0].chall, challenges.DNS01): - raise Exception("wrong challenge type left after validation") + raise(Exception("wrong challenge type left after validation")) def test_auth_deactivation_v2(): client = chisel2.make_client(None) @@ -1165,12 +1165,12 @@ def test_auth_deactivation_v2(): order = client.new_order(csr_pem) resp = client.deactivate_authorization(order.authorizations[0]) if resp.body.status is not messages.STATUS_DEACTIVATED: - raise Exception("unexpected authorization status") + raise(Exception("unexpected authorization status")) order = chisel2.auth_and_issue([random_domain()], client=client) resp = client.deactivate_authorization(order.authorizations[0]) if resp.body.status is not messages.STATUS_DEACTIVATED: - raise Exception("unexpected authorization status") + raise(Exception("unexpected authorization status")) def check_ocsp_basic_oid(cert_file, issuer_file, url): @@ -1189,8 +1189,8 @@ def check_ocsp_basic_oid(cert_file, issuer_file, url): expected = bytearray.fromhex("06 09 2B 06 01 05 05 07 30 01 01") for resp in responses: if not expected in bytearray(resp): - raise Exception("Did not receive successful OCSP response: %s doesn't contain %s" % - (base64.b64encode(resp), base64.b64encode(expected))) + raise(Exception("Did not receive successful OCSP response: %s doesn't contain %s" % + (base64.b64encode(resp), base64.b64encode(expected)))) expired_cert_name = "" @register_six_months_ago @@ -1213,17 +1213,19 @@ def ocsp_exp_unauth_setup(): def test_ocsp_exp_unauth(): tries = 0 + if expired_cert_name == "": + raise Exception("ocsp_exp_unauth_setup didn't run") while True: try: verify_ocsp(expired_cert_name, "test/test-ca2.pem", "http://localhost:4002", "XXX") - raise Exception("Unexpected return from verify_ocsp") + raise(Exception("Unexpected return from verify_ocsp")) except subprocess.CalledProcessError as cpe: - if cpe.output == 'Responder Error: unauthorized (6)\n': + if cpe.output == b'Responder Error: unauthorized (6)\n': break except: pass if tries is 5: - raise Exception("timed out waiting for unauthorized OCSP response for expired certificate") + raise(Exception("timed out waiting for unauthorized OCSP response for expired certificate")) tries += 1 time.sleep(0.25) @@ -1247,13 +1249,13 @@ def test_blocked_key_account(): terms_of_service_agreed=True)) except acme_errors.Error as e: if e.typ != "urn:ietf:params:acme:error:badPublicKey": - raise Exception("problem did not have correct error type, had {0}".format(e.typ)) + raise(Exception("problem did not have correct error type, had {0}".format(e.typ))) if e.detail != "public key is forbidden": - raise Exception("problem did not have correct error detail, had {0}".format(e.detail)) + raise(Exception("problem did not have correct error detail, had {0}".format(e.detail))) testPass = True if testPass is False: - raise Exception("expected account creation to fail with Error when using blocked key") + raise(Exception("expected account creation to fail with Error when using blocked key")) def test_blocked_key_cert(): # Only config-next has a blocked keys file configured. @@ -1276,13 +1278,13 @@ def test_blocked_key_cert(): order = client.poll_and_finalize(order) except acme_errors.Error as e: if e.typ != "urn:ietf:params:acme:error:badPublicKey": - raise Exception("problem did not have correct error type, had {0}".format(e.typ)) + raise(Exception("problem did not have correct error type, had {0}".format(e.typ))) if e.detail != "Error finalizing order :: invalid public key in CSR: public key is forbidden": - raise Exception("problem did not have correct error detail, had {0}".format(e.detail)) + raise(Exception("problem did not have correct error detail, had {0}".format(e.detail))) testPass = True if testPass is False: - raise Exception("expected cert creation to fail with Error when using blocked key") + raise(Exception("expected cert creation to fail with Error when using blocked key")) def run(cmd, **kwargs): return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs)