switch from external boulder to local pebble

This commit is contained in:
Brad Warren 2020-12-21 12:29:17 -08:00
parent 8e7353900c
commit 562610fd7e
8 changed files with 52 additions and 163 deletions

View file

@ -1,7 +1,6 @@
# letstest
Simple AWS testfarm scripts for certbot client testing
- Configures (canned) boulder server
- Launches EC2 instances with a given list of AMIs for different distros
- Copies certbot repo and puts it on the instances
- Runs certbot tests (bash scripts) on all of these
@ -56,11 +55,6 @@ It will take a minute for these instances to shut down and become available agai
A folder named `letest-<timestamp>` is also created with a log file from each instance of the test and a file named "results" containing the output above.
The tests take quite a while to run.
Also, the way all of the tests work is to check if there is already a boulder server running and if not start one. The boulder server is left running between tests,
and there are known issues if two instances of boulder attempt to be started. After starting your first test, wait until you see "Found existing boulder server:" or if you see output
about creating a boulder server, wait a minute before starting the 2nd test. You only have to do this after starting your first session of tests or after running
the `aws ec2 terminate-instances` command above.
## Scripts
Example scripts are in the 'scripts' directory, these are just bash scripts that have a few parameters passed
to them at runtime via environment variables. test_apache2.sh is a useful reference.
@ -73,5 +67,4 @@ See:
- https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html
Main repos:
- https://github.com/letsencrypt/boulder
- https://github.com/letsencrypt/letsencrypt

View file

@ -1,4 +1,7 @@
# These images are located in us-east-1.
#
# All machines must currently use x86_64 since Pebble does not currently
# publish images for other architectures.
targets:
#-----------------------------------------------------------------------------
@ -30,12 +33,6 @@ targets:
type: ubuntu
virt: hvm
user: admin
- ami: ami-0dcd54b7d2fff584f
name: debian10_arm64
type: ubuntu
virt: hvm
user: admin
machine_type: a1.medium
- ami: ami-003f19e0e687de1cd
name: debian9
type: ubuntu

View file

@ -1,7 +1,6 @@
"""
Certbot Integration Test Tool
- Configures (canned) boulder server
- Launches EC2 instances with a given list of AMIs for different distros
- Copies certbot repo and puts it on the instances
- Runs certbot tests (bash scripts) on all of these
@ -81,12 +80,6 @@ parser.add_argument('--saveinstances',
parser.add_argument('--alt_pip',
default='',
help="server from which to pull candidate release packages")
parser.add_argument('--killboulder',
action='store_true',
help="do not leave a persistent boulder server running")
parser.add_argument('--boulderonly',
action='store_true',
help="only make a boulder server")
cl_args = parser.parse_args()
# Credential Variables
@ -98,7 +91,6 @@ PROFILE = None if cl_args.aws_profile == 'SET_BY_ENV' else cl_args.aws_profile
# Globals
#-------------------------------------------------------------------------------
BOULDER_AMI = 'ami-072a9534772bec854' # premade shared boulder AMI 18.04LTS us-east-1
SECURITY_GROUP_NAME = 'certbot-security-group'
SENTINEL = None #queue kill signal
SUBNET_NAME = 'certbot-subnet'
@ -133,10 +125,6 @@ def make_security_group(vpc):
mysg = vpc.create_security_group(GroupName=SECURITY_GROUP_NAME,
Description='security group for automated testing')
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=22, ToPort=22)
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=80, ToPort=80)
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=443, ToPort=443)
# for boulder wfe (http) server
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=4000, ToPort=4000)
# for mosh
mysg.authorize_ingress(IpProtocol="udp", CidrIp="0.0.0.0/0", FromPort=60000, ToPort=61000)
return mysg
@ -193,23 +181,6 @@ def _get_block_device_mappings(ec2_client, ami_id):
# Helper Routines
#-------------------------------------------------------------------------------
def block_until_http_ready(urlstring, wait_time=10, timeout=240):
"Blocks until server at urlstring can respond to http requests"
server_ready = False
t_elapsed = 0
while not server_ready and t_elapsed < timeout:
try:
sys.stdout.write('.')
sys.stdout.flush()
req = urllib_request.Request(urlstring)
response = urllib_request.urlopen(req)
#if response.code == 200:
server_ready = True
except urllib_error.URLError:
pass
time.sleep(wait_time)
t_elapsed += wait_time
def block_until_ssh_open(ipstring, wait_time=10, timeout=120):
"Blocks until server at ipstring has an open port 22"
reached = False
@ -288,26 +259,15 @@ def deploy_script(cxn, scriptpath, *args):
args_str = ' '.join(args)
cxn.run('./'+scriptfile+' '+args_str)
def run_boulder(cxn):
boulder_path = '$GOPATH/src/github.com/letsencrypt/boulder'
cxn.run('cd %s && sudo docker-compose up -d' % boulder_path)
def config_and_launch_boulder(cxn, instance):
# yes, we're hardcoding the gopath. it's a predetermined AMI.
with cxn.prefix('export GOPATH=/home/ubuntu/gopath'):
deploy_script(cxn, 'scripts/boulder_config.sh')
run_boulder(cxn)
def install_and_launch_certbot(cxn, instance, boulder_url, target, log_dir):
def install_and_launch_certbot(cxn, instance, target, log_dir):
local_repo_to_remote(cxn, log_dir)
# This needs to be like this, I promise. 1) The env argument to run doesn't work.
# See https://github.com/fabric/fabric/issues/1744. 2) prefix() sticks an && between
# the commands, so it needs to be exports rather than no &&s in between for the script subshell.
with cxn.prefix('export BOULDER_URL=%s && export PUBLIC_IP=%s && export PRIVATE_IP=%s && '
with cxn.prefix('export PUBLIC_IP=%s && export PRIVATE_IP=%s && '
'export PUBLIC_HOSTNAME=%s && export PIP_EXTRA_INDEX_URL=%s && '
'export OS_TYPE=%s' %
(boulder_url,
instance.public_ip_address,
(instance.public_ip_address,
instance.private_ip_address,
instance.public_dns_name,
cl_args.alt_pip,
@ -344,7 +304,7 @@ def create_client_instance(ec2_client, target, security_group_id, subnet_id, sel
self_destruct=self_destruct)
def test_client_process(fab_config, inqueue, outqueue, boulder_url, log_dir):
def test_client_process(fab_config, inqueue, outqueue, log_dir):
cur_proc = mp.current_process()
for inreq in iter(inqueue.get, SENTINEL):
ii, instance_id, target = inreq
@ -366,7 +326,7 @@ def test_client_process(fab_config, inqueue, outqueue, boulder_url, log_dir):
with Connection(host_string, config=fab_config) as cxn:
try:
install_and_launch_certbot(cxn, instance, boulder_url, target, log_dir)
install_and_launch_certbot(cxn, instance, target, log_dir)
outqueue.put((ii, target, Status.PASS))
print("%s - %s SUCCESS"%(target['ami'], target['name']))
except:
@ -385,15 +345,13 @@ def test_client_process(fab_config, inqueue, outqueue, boulder_url, log_dir):
pass
def cleanup(cl_args, instances, targetlist, boulder_server, log_dir):
def cleanup(cl_args, instances, targetlist, log_dir):
print('Logs in ', log_dir)
# If lengths of instances and targetlist aren't equal, instances failed to
# start before running tests so leaving instances running for debugging
# isn't very useful. Let's cleanup after ourselves instead.
if len(instances) != len(targetlist) or not cl_args.saveinstances:
print('Terminating EC2 Instances')
if cl_args.killboulder:
boulder_server.terminate()
for instance in instances:
instance.terminate()
else:
@ -483,70 +441,18 @@ def main():
security_group_id = make_security_group(vpc).id
time.sleep(30)
boulder_preexists = False
boulder_servers = ec2_client.instances.filter(Filters=[
{'Name': 'tag:Name', 'Values': ['le-boulderserver']},
{'Name': 'instance-state-name', 'Values': ['running']}])
boulder_server = next(iter(boulder_servers), None)
print("Requesting Instances...")
if boulder_server:
print("Found existing boulder server:", boulder_server)
boulder_preexists = True
else:
print("Can't find a boulder server, starting one...")
# If we want to kill boulder on shutdown, have it self-destruct in case
# cleanup fails.
self_destruct = cl_args.killboulder
boulder_server = make_instance(ec2_client,
'le-boulderserver',
BOULDER_AMI,
KEYNAME,
machine_type='t2.micro',
#machine_type='t2.medium',
security_group_id=security_group_id,
subnet_id=subnet_id,
self_destruct=self_destruct)
instances = []
try:
if not cl_args.boulderonly:
print("Creating instances: ", end="")
# If we want to preserve instances, do not have them self-destruct.
self_destruct = not cl_args.saveinstances
for target in targetlist:
instances.append(
create_client_instance(ec2_client, target,
security_group_id, subnet_id,
self_destruct)
)
print()
# Configure and launch boulder server
#-------------------------------------------------------------------------------
print("Waiting on Boulder Server")
boulder_server = block_until_instance_ready(boulder_server)
print(" server %s"%boulder_server)
# host_string defines the ssh user and host for connection
host_string = "ubuntu@%s"%boulder_server.public_ip_address
print("Boulder Server at (SSH):", host_string)
if not boulder_preexists:
print("Configuring and Launching Boulder")
with Connection(host_string, config=fab_config) as boulder_cxn:
config_and_launch_boulder(boulder_cxn, boulder_server)
# blocking often unnecessary, but cheap EC2 VMs can get very slow
block_until_http_ready('http://%s:4000'%boulder_server.public_ip_address,
wait_time=10, timeout=500)
boulder_url = "http://%s:4000/directory"%boulder_server.private_ip_address
print("Boulder Server at (public ip): http://%s:4000/directory"%boulder_server.public_ip_address)
print("Boulder Server at (EC2 private ip): %s"%boulder_url)
if cl_args.boulderonly:
sys.exit(0)
print("Creating instances: ", end="")
# If we want to preserve instances, do not have them self-destruct.
self_destruct = not cl_args.saveinstances
for target in targetlist:
instances.append(
create_client_instance(ec2_client, target,
security_group_id, subnet_id,
self_destruct)
)
print()
# Install and launch client scripts in parallel
#-------------------------------------------------------------------------------
@ -564,7 +470,7 @@ def main():
# initiate process execution
client_process_args=(fab_config, inqueue, outqueue, boulder_url, log_dir)
client_process_args=(fab_config, inqueue, outqueue, log_dir)
for i in range(num_processes):
p = mp.Process(target=test_client_process, args=client_process_args)
jobs.append(p)
@ -615,7 +521,7 @@ def main():
sys.exit(1)
finally:
cleanup(cl_args, instances, targetlist, boulder_server, log_dir)
cleanup(cl_args, instances, targetlist, log_dir)
if __name__ == '__main__':

View file

@ -1,24 +0,0 @@
#!/bin/bash -x
# Configures and Launches Boulder Server installed on
# us-east-1 ami-072a9534772bec854 bouldertestserver3 (boulder commit b24fe7c3ea4)
# fetch instance data from EC2 metadata service
public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname)
public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4)
private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4)
# set to public DNS resolver
resolver_ip=8.8.8.8
resolver=$resolver_ip':53'
# modifies integration testing boulder setup for local AWS VPC network
# connections instead of localhost
cd $GOPATH/src/github.com/letsencrypt/boulder
# change test ports to real
sed -i '/httpPort/ s/5002/80/' ./test/config/va.json
sed -i '/httpsPort/ s/5001/443/' ./test/config/va.json
sed -i '/tlsPort/ s/5001/443/' ./test/config/va.json
# set dns resolver
sed -i 's/"127.0.0.1:8053",/"'$resolver'"/' ./test/config/va.json
sed -i 's/"127.0.0.1:8054"//' ./test/config/va.json

View file

@ -1,8 +0,0 @@
#!/bin/bash -x
# Check out special branch until latest docker changes land in Boulder master.
git clone -b docker-integration https://github.com/letsencrypt/boulder $BOULDERPATH
cd $BOULDERPATH
FAKE_DNS=$(ifconfig docker0 | grep "inet addr:" | cut -d: -f2 | awk '{ print $1}')
sed -i "s/FAKE_DNS: .*/FAKE_DNS: $FAKE_DNS/" docker-compose.yml
docker-compose up -d

View file

@ -64,11 +64,35 @@ if [ $? -ne 0 ] ; then
exit 1
fi
tools/venv3.py -e acme[dev] -e certbot[dev,docs] -e certbot-apache
tools/venv3.py -e acme[dev] -e certbot[dev,docs] -e certbot-apache -e certbot-ci
PEBBLE_LOGS="acme_server.log"
PEBBLE_URL="https://localhost:14000/dir"
venv3/bin/run_acme_server > "${PEBBLE_LOGS}" 2>&1 &
sudo "venv3/bin/certbot" -v --debug --text --agree-tos \
DumpPebbleLogs() {
if [ -f "${PEBBLE_LOGS}" ] ; then
echo "Pebble's logs were:"
cat "${PEBBLE_LOGS}"
fi
}
for n in $(seq 1 150) ; do
if curl --insecure "${PEBBLE_URL}" 2>/dev/null; then
break
else
echo "waiting for pebble"
sleep 1
fi
done
if ! curl --insecure "${PEBBLE_URL}" 2>/dev/null; then
echo "timed out waiting for pebble to start"
DumpPebbleLogs
exit 1
fi
sudo "venv3/bin/certbot" -v --debug --text --agree-tos --no-verify-ssl \
--renew-by-default --redirect --register-unsafely-without-email \
--domain $PUBLIC_HOSTNAME --server $BOULDER_URL
--domain "${PUBLIC_HOSTNAME}" --server "${PEBBLE_URL}"
if [ $? -ne 0 ] ; then
FAIL=1
fi
@ -90,7 +114,7 @@ fi
if [ "$OS_TYPE" = "ubuntu" ] ; then
export SERVER="$BOULDER_URL"
export SERVER="${PEBBLE_URL}"
"venv3/bin/tox" -e apacheconftest
else
echo Not running hackish apache tests on $OS_TYPE
@ -102,5 +126,6 @@ fi
# return error if any of the subtests failed
if [ "$FAIL" = 1 ] ; then
DumpPebbleLogs
exit 1
fi

View file

@ -1,7 +1,7 @@
#!/bin/bash -xe
set -o pipefail
# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL
# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME
# are dynamically set at execution
cd letsencrypt

View file

@ -1,7 +1,7 @@
#!/bin/bash -x
set -eo pipefail
# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution
# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME are dynamically set at execution
# with curl, instance metadata available from EC2 metadata service:
#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname)