Revert "integration: move to Python3 (#4313)" (#4323)

This reverts commit 796a7aa2f4.

People's tests have been breaking on `docker-compose up` with the following output:

```
ImportError: No module named requests
```

Fixes #4322
This commit is contained in:
Jacob Hoffman-Andrews 2019-07-03 11:35:45 -07:00 committed by GitHub
parent 0d9b48e280
commit 3af49a16be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 164 additions and 162 deletions

View File

@ -2,7 +2,7 @@ version: '3'
services:
boulder:
# To minimize fetching this should be the same version used below
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-06-28
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment:
FAKE_DNS: 10.77.77.77
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
@ -55,7 +55,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.12}:2019-06-28
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment:
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
@ -77,7 +77,7 @@ services:
logging:
driver: none
netaccess:
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-06-28
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment:
GO111MODULE: "on"
GOFLAGS: "-mod=vendor"

View File

@ -110,9 +110,9 @@ if [[ "$RUN" =~ "integration" ]] ; then
args+=("--skip-setup")
fi
source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv3}/bin/activate
source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv}/bin/activate
DIRECTORY=http://boulder:4000/directory \
python3 test/integration-test.py "${args[@]}"
python2 test/integration-test.py "${args[@]}"
fi
# Test that just ./start.py works, which is a proxy for testing that

View File

@ -9,5 +9,4 @@ 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

View File

@ -51,8 +51,6 @@ git clone https://github.com/certbot/certbot /certbot
cd /certbot
./letsencrypt-auto --os-packages-only
./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

View File

@ -1,4 +1,4 @@
import requests
import urllib2
import json
class ChallTestServer:
@ -39,14 +39,14 @@ class ChallTestServer:
self._baseURL = url
def _postURL(self, url, body):
return requests.post(
return urllib2.urlopen(
url,
data=json.dumps(body)).text
data=json.dumps(body)).read()
def _URL(self, path):
urlPath = self._paths.get(path, None)
if urlPath is None:
raise(Exception("No challenge test server URL path known for {0}".format(path)))
raise Exception("No challenge test server URL path known for {0}".format(path))
return self._baseURL + urlPath
def _clear_request_history(self, host, typ):

View File

@ -14,7 +14,7 @@ import socket
import sys
import threading
import time
import requests
import urllib2
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'.encode(),
'subjectAltName',
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,21 +126,19 @@ 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:
r = requests.get(authz.uri)
r.raise_for_status()
updated_authz = r.json()
updated_authz = json.loads(urllib2.urlopen(authz.uri).read())
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.decode())
f.write(pem)
return cert_resource
def http_01_answer(client, chall_body):
@ -225,7 +223,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)
@ -252,15 +250,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 as e:
print(e)
except messages.Error, e:
print e
sys.exit(1)

View File

@ -15,6 +15,7 @@ import sys
import signal
import threading
import time
import urllib2
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
@ -59,14 +60,14 @@ def make_client(email=None):
client.net.account = client.new_account(messages.NewRegistration.from_data(email=email,
terms_of_service_agreed=True))
else:
raise(Exception("Unrecognized terms of service URL %s" % tos))
raise Exception("Unrecognized terms of service URL %s" % tos)
return client
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.typ))
raise Exception("No %s challenge found" % typ.typ)
def make_csr(domains):
key = OpenSSL.crypto.PKey()
@ -99,7 +100,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:
order = client.poll_and_finalize(order)
@ -171,7 +172,7 @@ def expect_problem(problem_type, func):
if e.typ == problem_type:
ok = True
else:
raise(Exception("Expected %s, got %s" % (problem_type, error.__str__())))
raise Exception("Expected %s, got %s" % (problem_type, error.__str__()))
except acme_errors.ValidationError as e:
for authzr in e.failed_authzrs:
for chall in authzr.body.challenges:
@ -179,19 +180,19 @@ def expect_problem(problem_type, func):
if error and error.typ == problem_type:
ok = True
elif error:
raise(Exception("Expected %s, got %s" % (problem_type, error.__str__())))
raise Exception("Expected %s, got %s" % (problem_type, error.__str__()))
if not ok:
raise(Exception('Expected %s, got no error' % problem_type))
raise Exception('Expected %s, got no error' % problem_type)
if __name__ == "__main__":
# Die on SIGINT
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 as e:
print(e)
except messages.Error, e:
print e
sys.exit(1)

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python2.7
import base64
import os
import urllib
import urllib2
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).decode()
return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs)
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).decode()
ocsp_req_b64 = base64.b64encode(request_bytes)
# 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 = 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
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()
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, mode='rb') as f:
with open(ocsp_req_file) 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, "wb") as f:
with open(ocsp_resp_file, "w") 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():
requests.post("http://localhost:6789/debug/reset-purges", data="{}")
urllib2.urlopen("http://localhost:6789/debug/reset-purges", "{}")
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 = requests.get("http://localhost:6789/debug/get-purges")
purgeData = response.json()
raise Exception("Timed out waiting for Akamai purge")
response = urllib2.urlopen("http://localhost:6789/debug/get-purges")
purgeData = json.load(response)
if len(purgeData["V3"]) is not 1:
continue
break
@ -142,7 +142,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))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""
This file contains basic infrastructure for running the integration test cases.
@ -53,7 +53,7 @@ def run_expired_authz_purger():
tool = "expired-authz-purger"
out = get_future_output("./bin/expired-authz-purger --config cmd/expired-authz-purger/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
if CONFIG_NEXT:
@ -61,8 +61,8 @@ def run_expired_authz_purger():
else:
expected_output = 'Deleted a total of %d expired authorizations from %s' % (num, table)
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()
@ -121,9 +121,9 @@ def test_stats():
def expect_stat(port, stat):
url = "http://localhost:%d/metrics" % port
response = requests.get(url)
if not stat in response.text:
if not stat in response.content:
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"} ')
@ -156,7 +156,7 @@ def main():
args = parser.parse_args()
if not (args.run_all or 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 --all, --certbot, --chisel, --load or --custom"))
raise Exception("must run at least one of the letsencrypt or chisel tests with --all, --certbot, --chisel, --load or --custom")
if not args.skip_setup:
now = datetime.datetime.utcnow()
@ -169,13 +169,13 @@ def main():
now = datetime.datetime.utcnow()
twenty_days_ago = now+datetime.timedelta(days=-20)
if not startservers.start(race_detection=True, fakeclock=fakeclock(twenty_days_ago), config_dir=config):
raise(Exception("startservers failed (mocking twenty days ago)"))
raise Exception("startservers failed (mocking twenty days ago)")
v1_integration.caa_client = caa_client = chisel.make_client()
setup_twenty_days_ago()
startservers.stop()
if not startservers.start(race_detection=True):
raise(Exception("startservers failed"))
raise Exception("startservers failed")
if args.run_all or args.run_chisel:
run_chisel(args.test_case_filter)
@ -202,7 +202,7 @@ def main():
run_loadtest()
if not startservers.check():
raise(Exception("startservers.check failed"))
raise Exception("startservers.check failed")
global exit_status
exit_status = 0
@ -253,7 +253,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():
@ -263,7 +263,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():

View File

@ -1,4 +1,5 @@
import atexit
import BaseHTTPServer
import os
import shutil
import signal
@ -101,7 +102,7 @@ def start(race_detection, fakeclock=None, config_dir=default_config_dir):
print(e)
return False
print("All servers running. Hit ^C to kill.")
print "All servers running. Hit ^C to kill."
return True
def check():
@ -119,9 +120,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
@ -132,7 +133,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

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
import datetime
import json
@ -6,6 +7,7 @@ import random
import re
import requests
import time
import urllib2
import startservers
@ -38,7 +40,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()
@ -156,7 +158,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 = []
@ -164,7 +166,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)
@ -173,15 +175,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()
@ -218,7 +220,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 = []
@ -232,26 +234,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):
@ -307,7 +309,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
@ -331,7 +333,7 @@ def test_issuer():
of length exactly 1.
"""
certr, authzs = auth_and_issue([random_domain()])
cert = requests.get(certr.uri).content
cert = urllib2.urlopen(certr.uri).read()
# 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.
@ -374,7 +376,7 @@ def test_ct_submission():
def submissions(group):
count = 0
for log in group:
count += int(requests.get(log + "?hostnames=%s" % hostname).text)
count += int(urllib2.urlopen(log + "?hostnames=%s" % hostname).read())
return count
auth_and_issue([hostname])
@ -384,29 +386,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().decode(), '%Y%m%d%H%M%SZ')
expiry = datetime.datetime.strptime(cert.body.get_notAfter(), '%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)
requests.post("http://localhost:9381/clear", data='')
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, no_reminder))
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, first_reminder))
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, last_reminder))
resp = requests.get("http://localhost:9381/count?to=%s" % email_addr)
mailcount = int(resp.text)
urllib2.urlopen("http://localhost:9381/clear", data='')
print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, no_reminder)
print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, first_reminder)
print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, last_reminder)
resp = urllib2.urlopen("http://localhost:9381/count?to=%s" % email_addr)
mailcount = int(resp.read())
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()
@ -442,13 +444,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, ";")
@ -516,11 +518,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():
"""
@ -576,13 +578,13 @@ def test_admin_revoker_cert():
def test_sct_embedding():
certr, authzs = auth_and_issue([random_domain()])
certBytes = requests.get(certr.uri).content
certBytes = urllib2.urlopen(certr.uri).read()
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
@ -591,15 +593,15 @@ 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))

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""
Integration test cases for ACMEv2 as implemented by boulder-wfe2.
@ -64,7 +65,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 test_http_challenge_broken_redirect():
"""
@ -97,9 +98,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)
@ -217,7 +218,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 = []
@ -225,7 +226,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)
@ -234,15 +235,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()
@ -277,7 +278,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)
@ -287,7 +288,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 = []
@ -301,30 +302,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
@ -353,8 +354,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:
@ -419,8 +420,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",
@ -469,20 +470,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)
@ -583,13 +584,12 @@ def test_revoke_by_privkey():
def test_sct_embedding():
order = chisel2.auth_and_issue([random_domain()])
print(order.fullchain_pem.encode())
cert = x509.load_pem_x509_certificate(order.fullchain_pem.encode(), default_backend())
cert = x509.load_pem_x509_certificate(str(order.fullchain_pem), 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
@ -598,14 +598,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()
@ -624,7 +624,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({
@ -671,7 +671,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(u'(• ◡ •) <( VIPs only! )')
self.wfile.write(b'(• ◡ •) <( VIPs only! )')
BouncerHandler.guestlist = guestlist
BouncerHandler.redirect = redirect
@ -707,7 +707,7 @@ def multiva_setup(client, guestlist):
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")
@ -791,16 +791,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()
@ -827,7 +827,7 @@ def wait_for_tcp_server(addr, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((addr, port))
sock.sendall(b"\n")
sock.sendall("\n")
return
except socket.error:
time.sleep(0.5)
@ -869,9 +869,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()
@ -908,7 +908,7 @@ def test_z1_reuse():
if a.uri in authz_uris:
authz_uris.remove(a.uri)
if len(authz_uris) != 0:
raise(Exception("Failed to reuse all authzs. Remaining: %s" % authz_uris))
raise Exception("Failed to reuse all authzs. Remaining: %s" % authz_uris)
def test_new_order_policy_errs():
"""
@ -938,15 +938,18 @@ def test_new_order_policy_errs():
if e.detail != 'Error creating new order :: Cannot issue for "out-addr.in-addr.arpa": Policy forbids issuing for name (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"))
# 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")
except messages.Error as e:
if e.typ != "urn:ietf:params:acme:error:malformed":
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 run(cmd, **kwargs):
return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs)