http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/functions ---------------------------------------------------------------------- diff --git a/ci/tsqa/functions b/ci/tsqa/functions deleted file mode 100644 index f839108..0000000 --- a/ci/tsqa/functions +++ /dev/null @@ -1,303 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -NCPU=${NCPU:-$(( $(getconf _NPROCESSORS_ONLN) * 2 ))} # Numer of CPUs to build with -PORT=${PORT:-9090} # Initial listen port for Traffic Server -VALGRIND=${VALGRIND:-N} # Whether to run under valgrind -TMPDIR=${TMPDIR:-/tmp} # Scratch directory for test instance construction - -TSQA_FAIL=0 # Test failure count -TSQA_TESNAME=${TSQA_TESTNAME:-tsqa} # Name of current test -TSQA_ROOT=${TSQA_ROOT:-/tmp/$TSQA_TESTNAME.$RANDOM} # Filesystem root for current test -TSQA_TSXS=${TSQA_TSXS:-tsxs} - -# Print a log message/ -msg() { - echo "MSG:" "$@" -} - -msgwait() { - local secs="$1" - shift - - echo "MSG: waiting ${secs}s" "$@" - sleep $secs -} - -# Print a failure message and increment the failure count. -fail() { - TSQA_FAIL=$(($TSQA_FAIL + 1)) - echo "FAIL:" "$@" 1>&2 -} - -# Fail and exit. -fatal() { - TSQA_FAIL=$(($TSQA_FAIL + 1)) - echo "FATAL:" "$@" 1>&2 - exit $TSQA_FAIL -} - -# Run a command and silence any output on stderr. -quiet() { - "$@" 2>/dev/null -} - -# Run a command with all output redirected to the pest log file. -logexec() { - echo "$@" >> "$TSQA_ROOT/$TSQA_TESTNAME.log" 2>&1 - "$@" >> "$TSQA_ROOT/$TSQA_TESTNAME.log" 2>&1 -} - -tsxs() { - $TSQA_TSXS "$@" -} - -tsexec() { - local cmd="$1" - local run - shift - - case $VALGRIND in - y|yes|Y|YES|1) run="valgrind --trace-children=yes --trace-children-skip=env env" ;; - *) run=env ;; - esac - - # XXX enabling MallocStackLogging on all processes is annoying - # because it logs 3 lines to stderr every time. We generally only - # want leaks detection on traffic_server, so this is a bit of a - # waste ... - - # MALLOC_CHECK_=2 => enable glibc malloc checking, abort on error - # MallocStackLogging=1 => record OS X malloc stacks for leak checking - $run \ - MALLOC_CHECK_=2 \ - MallocErrorAbort=1 \ - TS_ROOT=$TSQA_ROOT \ - $(bindir)/$cmd "$@" -} - -reconfigure() { - local srcdir="$1" - msg running autoreconf in $srcdir ... - ( - cd "$srcdir" - autoreconf -i - ) > autoreconf.log 2>&1 -} - -install () { - [[ -d $BUILD ]] && rm -rf $BUILD - [[ -d $PREFIX ]] && rm -rf $PREFIX - - msg installing ... - mkdir -p $BUILD && ( - cd $BUILD - $SRC/configure \ - --prefix=$PREFIX \ - --with-user=$(id -un) \ - --enable-debug \ - CCFLAGS=-O0 CXXFLAGS=-O0 - make -j $NCPU && make install - ) > /dev/null - - msg installed to $PREFIX -} - -logdir() { - local prefix=$(tsxs -q PREFIX) - tsxs -q LOGDIR | sed -es+$prefix/++ -} - -runtimedir() { - local prefix=$(tsxs -q PREFIX) - tsxs -q RUNTIMEDIR | sed -es+$prefix/++ -} - -sysconfdir() { - local prefix=$(tsxs -q PREFIX) - tsxs -q SYSCONFDIR | sed -es+$prefix/++ -} - -bindir() { - tsxs -q BINDIR -} - -# pidof(name): echo the pid of the given process name -pidof() { - case "$1" in - cop|manager|server);; - *) fatal no such process name: $1 - esac - quiet cat $TSQA_ROOT/$(runtimedir)/${1}.lock -} - -# alive(name): Test whether the process "name" is alive. -alive() { - local pid=$(pidof $1) - if [[ ! -z "$pid" ]] ; then - quiet kill -0 $pid - return $? - fi - - false -} - -# Start up Traffic Server. Test for all the processes so that we have a better -# chance of delaying the test until traffic_server is ready. -startup() { - local log=$TSQA_ROOT/$(logdir)/cop.log - ( tsexec traffic_cop --stdout > $log )& - for proc in cop manager server; do - for i in $(seq 10) ; do - alive $proc && msg $proc is alive && break - sleep 1 - done - done - - # And a final sleep to let traffic_server come up ... - sleep 2 -} - -# Shut down Traffic Server. -shutdown() { - - # Quick'n'dirty cleanup of background jobs. - jobs -p | while read pid ; do - kill $pid - done - - local pid=$(pidof cop) - if [[ -z "$pid" ]] ; then - return - fi - -# XXX If we are on Darwin, we can check the traffic_server for leaks before shutting down, but -# we really only want to do this for traffic_server ... or we should sink the output to the test -# log and fail on the exit status. - -# if [ -x /usr/bin/leaks ]; then -# msg checking for leaks ... -# /usr/bin/leaks $(pidof server) -# fi - - msg shutting down ... - while quiet kill -0 $pid ; do - quiet kill -TERM $pid - pid=$(pidof cop) - if [[ -z "$pid" ]] ; then - return - fi - done - - exit $TSQA_FAIL -} - -restart() { - local pid=$(pidof cop) - if [[ -z "$pid" ]] ; then - return - fi - - msg shutting down ... - while quiet kill -0 $pid ; do - quiet kill -TERM $pid - pid=$(pidof cop) - if [[ -z "$pid" ]] ; then - return - fi - done - - startup -} - -# Test for Traffic Server crash logs. -crash() { - local outfile="$TSQA_ROOT/$(logdir)/traffic.out" - - msg checking for crashes ... - for i in $(seq 10); do - sleep 1 - [[ -e $outfile ]] && \ - grep -a -A 10 "STACK TRACE" $outfile && \ - fail detected a crash - done -} - -# Bootstrap a TSQA test root. The result of this is an independent test root -# that contains all the variable parts of a traffic server configuration, while -# referring to the parent installation for the actual test binaries. -bootstrap() { - local prefix=$(tsxs -q PREFIX) - local sysconfdir=$(tsxs -q SYSCONFDIR | sed -es+$prefix/++) - local dir - local userid=${SUDO_USER:-$(whoami)} - - # Create runtime directories in the test root. - for dir in SYSCONFDIR LOCALSTATEDIR RUNTIMEDIR LOGDIR ; do - local p=$(tsxs -q $dir | sed -es+$prefix/++) - mkdir -p $TSQA_ROOT/$p - done - - # Copy config across - cp -r $(tsxs -q SYSCONFDIR)/*.{config,xml} $TSQA_ROOT/$sysconfdir - - # Delete any config variables we are about to set. - sed -i.orig \ - -e/proxy.config.body_factory.template_sets_dir/d \ - -e/proxy.config.plugin.plugin_dir/d \ - -e/proxy.config.bin_path/d \ - -e/proxy.config.admin.user_id/d \ - -e/proxy.config.diags/d \ - -e/proxy.config.http.server_ports/d \ - -e/proxy.config.config_update_interval_ms/d \ - $TSQA_ROOT/$sysconfdir/records.config || fatal failed to initialize records.config - - cat >> $TSQA_ROOT/$sysconfdir/records.config <<EOF -CONFIG proxy.config.bin_path STRING $(tsxs -q BINDIR) -CONFIG proxy.config.plugin.plugin_dir STRING $(tsxs -q LIBEXECDIR) -CONFIG proxy.config.body_factory.template_sets_dir STRING $(tsxs -q SYSCONFDIR)/body_factory - -CONFIG proxy.config.admin.user_id STRING $userid -CONFIG proxy.config.http.server_ports STRING $PORT - -# Flush config updates every 0.5s so that we don't have to sleep so long making config changes. -CONFIG proxy.config.config_update_interval_ms INT 500 - -# Send all diagnostics to both traffic.out and diags.log. -CONFIG proxy.config.diags.output.diag STRING OL -CONFIG proxy.config.diags.output.debug STRING OL -CONFIG proxy.config.diags.output.status STRING OL -CONFIG proxy.config.diags.output.note STRING OL -CONFIG proxy.config.diags.output.warning STRING OL -CONFIG proxy.config.diags.output.error STRING OL -CONFIG proxy.config.diags.output.fatal STRING OL -CONFIG proxy.config.diags.output.alert STRING OL -CONFIG proxy.config.diags.output.emergency STRING OL - -CONFIG proxy.config.diags.debug.enabled INT 1 -CONFIG proxy.config.diags.debug.tags STRING NULL -CONFIG proxy.config.diags.show_location INT 1 -EOF - - chown -R "$userid" $TSQA_ROOT - - msg bootstrapped Traffic Server into $TSQA_ROOT - tsexec traffic_layout -} - -# vim: set sw=2 ts=2 et :
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/requirements.txt ---------------------------------------------------------------------- diff --git a/ci/tsqa/requirements.txt b/ci/tsqa/requirements.txt new file mode 100644 index 0000000..5897503 --- /dev/null +++ b/ci/tsqa/requirements.txt @@ -0,0 +1,6 @@ +# requirements for the python virtualenv + +# TODO: pin a specific version +https://github.com/apache/trafficserver-qa/archive/master.zip +pyyaml +pyOpenSSL http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/run_all.sh ---------------------------------------------------------------------- diff --git a/ci/tsqa/run_all.sh b/ci/tsqa/run_all.sh deleted file mode 100755 index d2f04f2..0000000 --- a/ci/tsqa/run_all.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/sh -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -OK=() -FAIL=() -EXCLUDE=() -STATUS=0 - -# Produce a help page -do_help() { - echo "run_all.sh: Run all TSQA tests" - echo - echo "Options:" - echo " -e Exclude the given test" - echo " -h Show this help page" -} - -# Parse the arguments -while getopts "e:" opt; do - case $opt in - e) - EXCLUDE+=($OPTARG) - ;; - \?|h) - do_help - exit 1 - ;; - esac -done - - -# Run all tests, record the results -for test in test-*; do - run_it=1 - for ex in ${EXCLUDE[@]}; do - echo $ex - if [ "$ex" == "$test" ]; then - run_it=0 - break - fi - done - if [ $run_it -ne 0 ]; then - echo "--> Starting test: $test" - ./${test} - res=$? - if [ $res != 0 ]; then - echo "Failure: ${test}" - FAIL+=(${test}) - STATUS=1 - else - echo "Success: ${test}" - OK+=(${test}) - fi - fi -done - - -# Print out a results summary -echo -echo -echo "RESULT SUMMARY" -echo "==============" -for t in ${OK[@]}; do - echo "$t ...OK" -done -for t in ${FAIL[@]}; do - echo "$t ...FAIL" -done - -exit ${STATUS} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-bootstrap ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-bootstrap b/ci/tsqa/test-bootstrap deleted file mode 100755 index 4f817b4..0000000 --- a/ci/tsqa/test-bootstrap +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -# This test verifies that we can start Traffic Server in the test root. It is primarily -# intended as a trivial payload to test the TSQA harness. -check() { - - msg waiting ... - sleep 2 -} - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -check - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-log-configuration ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-log-configuration b/ci/tsqa/test-log-configuration deleted file mode 100755 index 3393c23..0000000 --- a/ci/tsqa/test-log-configuration +++ /dev/null @@ -1,68 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -COUNT=${COUNT:-10} -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -# Make up an arbitratry URL to generate error logs. We use the loopback address -# number because I've seen "localhost" resolve to an IPv6 address. That sucked. -URL=http://127.0.0.1:$PORT - -# This test verifies that online reloading of the logging configuration -# does not crash Traffic Server. -check() { - local logdir=$(logdir) - - for i in $(seq $COUNT) ; do - msg check $i ... - curl --silent --show-error -o /dev/null -H "Host: not there" $URL - tsexec traffic_line -s proxy.config.diags.debug.tags -v 'log-.*' - tsexec traffic_line -s proxy.config.diags.debug.enabled -v 1 - tsexec traffic_line -x - msgwait 2 - curl --silent --show-error -o /dev/null -H "Host: not there" $URL - # Set a harmless proxy.config.log.hostname to trigger a config reload ... - tsexec traffic_line -s proxy.config.log.hostname -v jpeach-test-$$-$i - tsexec traffic_line -s proxy.config.log.search_top_sites -v $i - tsexec traffic_line -x - msgwait 2 - curl --silent --show-error -o /dev/null -H "Host: not there" $URL - crash - - # Verify that we have all the error logs that we expect. - for logfile in error.log diags.log manager.log traffic.out ; do - [[ -e $TSQA_ROOT/$logdir/$logfile ]] || fatal $logfile is missing - done - - done -} - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -msgwait 6 -check - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-log-refcounting ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-log-refcounting b/ci/tsqa/test-log-refcounting deleted file mode 100755 index 41bd312..0000000 --- a/ci/tsqa/test-log-refcounting +++ /dev/null @@ -1,112 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -COUNT=${COUNT:-100} -SERVER_PORT=${SERVER_PORT:-4000} -RELOAD_INTERVAL_SECS=${RELOAD_INTERVAL_SECS:-30} - -bootstrap - -# Force a logging subsystem reload. -reload() { - while sleep $RELOAD_INTERVAL_SECS ; do - msg reloading logging configuration - tsexec traffic_line -s proxy.config.log.hostname -v "$(date)" - tsexec traffic_line -x - done -} - -if [ ! -x $(bindir)/jtest ] ; then - fatal "missing jtest program; rebuild with --enable-test-tools" -fi - -cat >$TSQA_ROOT/$(sysconfdir)/remap.config <<REMAP -map http://jtest.trafficserver.apache.org:$SERVER_PORT http://127.0.0.1:$SERVER_PORT -REMAP - -# Configure the tcpinfo plugin so that we have some API log objects. -cat >$TSQA_ROOT/$(sysconfdir)/plugin.config <<REMAP -tcpinfo.so --log-file=tcpinfo1 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2 -tcpinfo.so --log-file=tcpinfo2 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2 -tcpinfo.so --log-file=tcpinfo3 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2 -tcpinfo.so --log-file=tcpinfo4 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2 -REMAP - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -# Wait for traffic_manager to start. -alive manager -alive server -msgwait 2 - -# Logging configuration ... -tsexec traffic_line -s proxy.config.log.max_space_mb_for_logs -v 10 -tsexec traffic_line -s proxy.config.log.max_space_mb_for_orphan_logs -v 10 -tsexec traffic_line -s proxy.config.log.squid_log_enabled -v 1 -tsexec traffic_line -s proxy.config.log.squid_log_is_ascii -v 1 -# Roll every megabyte ... -tsexec traffic_line -s proxy.config.log.rolling_interval_sec -v 60 -tsexec traffic_line -s proxy.config.log.rolling_size_mb -v 1 -# Don't declare log space exhausted until there is < 1MB free. -tsexec traffic_line -s proxy.config.log.max_space_mb_headroom -v 1 -# Flush log buffers every second. -tsexec traffic_line -s proxy.config.log.max_secs_per_buffer -v 1 - -# Enable logging diagnostics for test debugging. If you enable this -# for the test itself, the diagnostics log will exhaust all the logging -# space and everything will go to hell. -#tsexec traffic_line -s proxy.config.diags.debug.tags -v log - -# The sleep is needed to let Traffic Server schedule the config change. -msgwait 4 to restart with updated logging configuration -# XXX: this needs a full bounce -tsexec traffic_line -L - -# Wait for traffic_manager to start. -alive manager -alive server -msgwait 10 - -msg starting config reload every $RELOAD_INTERVAL_SECS seconds -reload& - -for i in $(seq $COUNT) ; do - msg $(bindir)/jtest --proxy_port $PORT --proxy_host 127.0.0.1 \ - --server_port $SERVER_PORT --server_host jtest.trafficserver.apache.org \ - --clients 10 --test_time 60 - $(bindir)/jtest --proxy_port $PORT --proxy_host 127.0.0.1 \ - --server_port $SERVER_PORT --server_host jtest.trafficserver.apache.org \ - --clients 10 --test_time 60 - - if [ -r $(logdir)/squid.log ]; then - fatal squid.log is missing - fi - - # Check for a crash ... - crash -done - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-multicert-loading ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-multicert-loading b/ci/tsqa/test-multicert-loading deleted file mode 100755 index cc0e49e..0000000 --- a/ci/tsqa/test-multicert-loading +++ /dev/null @@ -1,81 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# test-multicert-loading: test loading large numbers of SSL certificates. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -#msg unpacking the SSL certificates into \$sysconfdir/ssl. -#( -# tarball=$(cd $(dirname $0) && pwd)/ssl-multicert-bundle.tar.bz2 -# conf=$TSQA_ROOT/$(sysconfdir)/ssl -# -# cd $TSQA_ROOT/$(sysconfdir) && tar -xf $tarball -#) - -msg updating SSL configuration paths -tsexec traffic_line -s proxy.config.ssl.server.cert.path -v $TSQA_ROOT/$(sysconfdir)/ssl -tsexec traffic_line -s proxy.config.ssl.server.multicert.filename -v $TSQA_ROOT/$(sysconfdir)/ssl/ssl_multicert.config - -# XXX configure an exampe plugin that uses the TS-2437 SSL lifecycle hooks - -# XXX hardcoding the ports is lame ... -PORT=9443:ssl,10443:ssl,11443:ssl - -# Enable SSL and bounce Traffic Server. -tsexec traffic_line -s proxy.config.diags.action.enabled -v 1 -tsexec traffic_line -s proxy.config.diags.action.tags -v test.multicert.delay - -tsexec traffic_line -s proxy.config.http.server_ports -v $PORT -tsexec traffic_line -s proxy.config.diags.debug.enabled -v 1 -tsexec traffic_line -s proxy.config.diags.debug.tags -v ssl - -# Stash the admin port while we have traffic_server running. It won't be -# available later if traffic_server does not come back up. -admin_port=$(tsexec traffic_line -r proxy.config.process_manager.mgmt_port) - -# The sleep is needed to let Traffic Server schedule the config change. -msgwait 2 to restart with SSL ports enabled -tsexec traffic_line -L - -msgwait 6 for traffic_server to restart -alive server || startup || fatal unable to start Traffic Server - -# XXX use the SSL lifecycle hooks in TS-2437 to verify that we loaded the -# certificates. - -START=$(date +%s) - -# Verify that the healthcheck comes up within about 60 seconds. -for c in $(seq 60) ; do - curl --silent --max-time 1 -o /dev/null http://127.0.0.1:${admin_port}/synthetic.txt && exit $TSQA_FAIL - sleep 1 -done - -fail unable to start traffic_server after $(( $(date +%s) - $START )) seconds -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-privilege-elevation ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-privilege-elevation b/ci/tsqa/test-privilege-elevation deleted file mode 100755 index 2d4eda0..0000000 --- a/ci/tsqa/test-privilege-elevation +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -COUNT=${COUNT:-10} -source $(dirname $0)/functions - -# This test verifies Traffic Server can elevate privileges correctly, based on -# the configuration settings: -# -# proxy.config.ssl.cert.load_elevated -# proxy.config.plugin.load_elevated - -check() { - - for i in $(seq $COUNT) ; do - msg check $i ... - touch $TSQA_ROOT/$(sysconfdir)/remap.config - touch $TSQA_ROOT/$(sysconfdir)/ssl_multicert.config - tsexec traffic_line -x - msgwait 2 - done - - crash -} - -if [ x"$(id -u)" != x"0" ] ; then - fatal this test needs to be run as root -fi - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -tsexec traffic_line -s proxy.config.ssl.cert.load_elevated -v 1 -tsexec traffic_line -s proxy.config.plugin.load_elevated -v 1 -tsexec traffic_line -s proxy.config.diags.debug.tags -v 'privileges' -tsexec traffic_line -s proxy.config.diags.debug.enabled -v 1 - -cat >$TSQA_ROOT/$(sysconfdir)/remap.config <<REMAP -# Add a remap rule, it doesn't matter what it is .. -map http://jtest.trafficserver.apache.org http://127.0.0.1 \ - @plugin=conf_remap.so @pparam=proxy.config.url_remap.pristine_host_hdr=1 -REMAP - -cat >$TSQA_ROOT/$(sysconfdir)/plugin.config <<PLUGIN -# Add a plugin, it doesn't matter which one. A better test would load -# a plugin that requires elevated access, and checks for it in the -# plugin interface. - -xdebug.so -PLUGIN - -# The sleep is needed to let Traffic Server schedule the config change. -msgwait 4 to restart load elevation enabled -tsexec traffic_line -L - -msgwait 6 for traffic_server to restart -alive server || startup || fatal unable to start Traffic Server - -check - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-server-intercept ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-server-intercept b/ci/tsqa/test-server-intercept deleted file mode 100755 index ee1eab3..0000000 --- a/ci/tsqa/test-server-intercept +++ /dev/null @@ -1,80 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -# The intercept example plugin is hard-coded to port 60000. -SERVER_PORT=${SERVER_PORT:-60000} - -# Use traffic_line -m to list all the configuration variables. Verify their values with -# traffic_line -r. This tests the TSRecordGet() and TSRecordGetMatchMult() remote APIs. -check() { - local key - local val1 - local val2 - - local bindir=$(bindir) - - tsexec traffic_line -m proxy.config | while read key val1 ; do - val2=$(TS_ROOT=$TSQA_ROOT $bindir/traffic_line -r $key) - if [ "$?" != "0" ]; then - fail failed to fetch value for $key - elif [ "$val1" != "$val1" ] ; then - fail value mismatch for $key, expected:\"$val1\", received:\"$val2\" - fi - done -} - -bootstrap - -if [ ! -x $(bindir)/jtest ] ; then - fatal "missing jtest program; rebuild with --enable-test-tools" -fi - -cat >$TSQA_ROOT/$(sysconfdir)/remap.config <<REMAP -map http://jtest.trafficserver.apache.org:$SERVER_PORT http://127.0.0.1:$SERVER_PORT -REMAP - -cat >$TSQA_ROOT/$(sysconfdir)/plugin.config <<REMAP -#intercept.so -REMAP - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -# Wait for traffic_manager to start. -alive manager -alive server -msgwait 1 - -# Run jtest for a while to exercise the intercept path. Note that jtest only exercises -# one of many possible sequences of events. Specifically, the jtest server will always -# close the socket before the client finishes reasing the response. -$(bindir)/jtest --proxy_port $PORT --proxy_host 127.0.0.1 \ - --server_port $SERVER_PORT --server_host jtest.trafficserver.apache.org \ - --clients 10 --test_time 60 - -# Check for a crash ... -crash - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-ssl-certificates ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-ssl-certificates b/ci/tsqa/test-ssl-certificates deleted file mode 100755 index e12b798..0000000 --- a/ci/tsqa/test-ssl-certificates +++ /dev/null @@ -1,153 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -OPENSSL=${OPENSSL:-openssl} - -check_openssl_version() { - local vers=$($OPENSSL version) - - # Apparantly OpenSSL SNI was added in 0.9.8f, but I'm too lazy to - # parse the version properly, so let's just say that 1.0 and greater is ok. - case $vers in - OpenSSL\ [1-9].[0-9].*) return 0;; - *) return 1;; - esac -} - -make_ssl_certificate() { - local cn="$1" - local filename="$2" - - msg generating SSL key and certificate for "$cn" - - $OPENSSL genrsa -out ${cn}.key 2048 - $OPENSSL req -new -key ${cn}.key -out ${cn}.csr \ - -subj "/C=US/ST=CA/L=Norm/O=TrafficServer/OU=Test/CN=${cn}" - $OPENSSL x509 -req -days 1 -in ${cn}.csr -signkey ${cn}.key -out ${cn}.crt - - cat ${cn}.crt ${cn}.key > ${filename} - - rm -rf ${cn}.csr ${cn}.key ${cn}.crt -} - -openssl_verify_certificate() { - local certname="$1" # Certificate CN to expect - local result="$TSQA_ROOT/${certname}.result" - local commonName= - local status=1 # default status is FAIL - - shift - msg "checking for the $certname certificate ..." | tee -a "$TSQA_ROOT/$TSQA_TESTNAME.log" - - # When s_client verifies the certificate, it will log a line that looks like: - # depth=0 C = US, ST = CA, L = Norm, O = TrafficServer, OU = Test, CN = address.tsqa.trafficserver.apache.org - $OPENSSL s_client "$@" < /dev/null > "$result" 2>&1 - if [ "$?" != 0 ]; then - fail "openssl check for $certname failed" - fi - - # The output of this openssl formulation is: - # subject= - # countryName = US - # stateOrProvinceName = CA - # localityName = Norm - # organizationName = TrafficServer - # organizationalUnitName = Test - # commonName = *.tsqa.trafficserver.apache.org - commonName=$($OPENSSL x509 -in "$result" -noout -subject -nameopt multiline | awk '/commonName/{print $3}') - - if [ "$commonName" != "$certname" ]; then - fail "received certificate CN \"$commonName\", expected \"$certname\"" - fi - - if [ "$?" != 0 ]; then - fail "certificate name $certname did not match" - fi -} - -check_openssl_version || fatal OpenSSL 1.0 or later is required - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -for name in \ - \*.tsqa.trafficserver.apache.org \ - sni.tsqa.trafficserver.apache.org \ - port.tsqa.trafficserver.apache.org \ - address.tsqa.trafficserver.apache.org \ - default.tsqa.trafficserver.apache.org -do - logexec make_ssl_certificate $name $TSQA_ROOT/$(sysconfdir)/${name}.pem \ - || fatal failed to generate SSL certificate for "$name" -done - -cat > $TSQA_ROOT/$(sysconfdir)/ssl_multicert.config <<EOF -ssl_cert_name=sni.tsqa.trafficserver.apache.org.pem -ssl_cert_name=*.tsqa.trafficserver.apache.org.pem - -ssl_cert_name=port.tsqa.trafficserver.apache.org.pem dest_ip=127.0.0.1:10443 -ssl_cert_name=address.tsqa.trafficserver.apache.org.pem dest_ip=127.0.0.1 -ssl_cert_name=default.tsqa.trafficserver.apache.org.pem dest_ip=* -EOF - -# XXX hardcoding the ports is lame ... -PORT=9443:ssl,10443:ssl,11443:ssl - -# Enable SSL and bounce Traffic Server. -tsexec traffic_line -s proxy.config.http.server_ports -v $PORT -tsexec traffic_line -s proxy.config.diags.debug.tags -v ssl - -# The sleep is needed to let Traffic Server schedule the config change. -msgwait 2 to restart with SSL ports enabled -tsexec traffic_line -L - -msgwait 6 for traffic_server to restart -alive server || startup || fatal unable to start Traffic Server - -# debugging ... -# tsexec traffic_line -r proxy.config.diags.debug.tags -# tsexec traffic_line -r proxy.config.http.server_ports - -# This should get *.tsqa.trafficserver.apache.org ... -openssl_verify_certificate '*.tsqa.trafficserver.apache.org' \ - -connect 127.0.0.1:9443 -servername wildcard.tsqa.trafficserver.apache.org - -# This should get sni.tsqa.trafficserver.apache.org ... -openssl_verify_certificate 'sni.tsqa.trafficserver.apache.org' \ - -connect 127.0.0.1:9443 -servername sni.tsqa.trafficserver.apache.org - -# This should get port.tsqa.trafficserver.apache.org ... -openssl_verify_certificate 'port.tsqa.trafficserver.apache.org' \ - -connect 127.0.0.1:10443 - -# This should get address.tsqa.trafficserver.apache.org ... -openssl_verify_certificate 'address.tsqa.trafficserver.apache.org' \ - -connect 127.0.0.1:9443 - -# XXX not sure how to get the default.tsqa.trafficserver.apache.org; need to listen on a second address for that. - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/test-trafficline-metrics ---------------------------------------------------------------------- diff --git a/ci/tsqa/test-trafficline-metrics b/ci/tsqa/test-trafficline-metrics deleted file mode 100755 index 1f28cbd..0000000 --- a/ci/tsqa/test-trafficline-metrics +++ /dev/null @@ -1,56 +0,0 @@ -#! /usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs} -TSQA_TESTNAME=$(basename $0) -source $(dirname $0)/functions - -# Use traffic_line -m to list all the configuration variables. Verify their values with -# traffic_line -r. This tests the TSRecordGet() and TSRecordGetMatchMult() remote APIs. -check() { - local key - local val1 - local val2 - - local bindir=$(bindir) - - tsexec traffic_line -m proxy.config | while read key val1 ; do - val2=$(TS_ROOT=$TSQA_ROOT $bindir/traffic_line -r $key) - if [ "$?" != "0" ]; then - fail failed to fetch value for $key - elif [ "$val1" != "$val1" ] ; then - fail value mismatch for $key, expected:\"$val1\", received:\"$val2\" - fi - done -} - -bootstrap - -# If Traffic Server is not up, bring it up ... -alive cop || startup || fatal unable to start Traffic Server -trap shutdown 0 EXIT - -# Wait for traffic_manager to start. -alive manager -msgwait 1 - -check - -exit $TSQA_FAIL - -# vim: set sw=2 ts=2 et : http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/helpers.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/helpers.py b/ci/tsqa/tests/helpers.py new file mode 100644 index 0000000..7333569 --- /dev/null +++ b/ci/tsqa/tests/helpers.py @@ -0,0 +1,57 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import tempfile + +import tsqa.environment +import tsqa.test_cases +import tsqa.utils + +unittest = tsqa.utils.import_unittest() + +# TODO: check that the given path is relative +def tests_file_path(path): + ''' + Return the absolute path to a file with relative path "name" from tsqa/files + ''' + base = os.path.realpath(os.path.join(__file__, '..', '..', 'files')) + return os.path.join(base, path) + +class EnvironmentCase(tsqa.test_cases.EnvironmentCase): + ''' + This class will get an environment (which is unique) but won't start it + ''' + @classmethod + def getEnv(cls): + ''' + This function is responsible for returning an environment + ''' + SOURCE_DIR = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) + TMP_DIR = os.path.join(tempfile.gettempdir(), 'tsqa') + ef = tsqa.environment.EnvironmentFactory(SOURCE_DIR, + os.path.join(TMP_DIR, 'base_envs'), + default_configure={'enable-example-plugins': None, + 'enable-test-tools': None, + 'disable-dependency-tracking': None, + 'enable-ccache': None, + }, + ) + # TODO: figure out a way to determine why the build didn't fail and + # not skip all build failures? + try: + return ef.get_environment(cls.environment_factory.get('configure'), cls.environment_factory.get('env')) + except Exception as e: + raise unittest.SkipTest(e) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_buildoptions.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_buildoptions.py b/ci/tsqa/tests/test_buildoptions.py new file mode 100644 index 0000000..2279062 --- /dev/null +++ b/ci/tsqa/tests/test_buildoptions.py @@ -0,0 +1,61 @@ +''' +Test that configuration options successfully compile +''' + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import requests +import time +import subprocess +import logging + +import helpers +import tsqa.test_cases +import tsqa.utils + +log = logging.getLogger(__name__) + +class TestBuildOption(helpers.EnvironmentCase): + ''' + Run the built-in traffic_server regression test suite. + ''' + + def test_buildoption(self): + pass + +class TestBuildOptionFastSDK(TestBuildOption): + '''Build with --enable-fast-sdk''' + environment_factory = { 'configure': { 'enable-fast-sdk': None }, } + +class TestBuildOptionDisableDiags(TestBuildOption): + '''Build with --disable-diags''' + environment_factory = { 'configure': { 'disable-diags': None }, } + +class TestBuildOptionDisableTests(TestBuildOption): + '''Build with --disable-tests''' + environment_factory = { 'configure': { 'disable-tests': None }, } + +class TestBuildOptionEnableStaticProxy(TestBuildOption): + '''Build with --enable-static-proxy''' + environment_factory = { 'configure': { 'enable-static-proxy': None }, } + +class TestBuildOptionEnableCxxApi(TestBuildOption): + '''Build with --enable-cppapi''' + environment_factory = { 'configure': { 'enable-cppapi': None }, } + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_chunked.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_chunked.py b/ci/tsqa/tests/test_chunked.py new file mode 100644 index 0000000..9b7fdc0 --- /dev/null +++ b/ci/tsqa/tests/test_chunked.py @@ -0,0 +1,196 @@ +''' +Test chunked request/responses +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import requests +import time +import logging +import json +import uuid +import socket + +import helpers + +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +log = logging.getLogger(__name__) + +import SocketServer +class ChunkedHandler(SocketServer.BaseRequestHandler): + """ + A subclass of RequestHandler which return chunked encoding optionally + + /parts/sleep_time/close + parts: number of parts to send + sleep_time: time between parts + close: bool wether to close properly + """ + + def handle(self): + # Receive the data in small chunks and retransmit it + conn_id = uuid.uuid4().hex + while True: + data = self.request.recv(4096).strip() + if data: + log.info('sending data back to the client') + else: + log.info('Client disconnected') + break + inc_lines = data.splitlines() + try: + uri = inc_lines[0].split()[1] + except IndexError: + break + parts = 5 # how many things to send + sleep_time = 0.2 # how long to sleep between parts + close = True # whether to close properly + if uri[1:]: # if there is something besides / + uri_parts = uri[1:].split('/') + if len(uri_parts) >= 1: + parts = int(uri_parts[0]) + if len(uri_parts) >= 2: + sleep_time = float(uri_parts[1]) + if len(uri_parts) >= 3: + close = json.loads(uri_parts[2]) + resp = ('HTTP/1.1 200 OK\r\n' + 'X-Conn-Id: ' + str(conn_id) + '\r\n' + 'Transfer-Encoding: chunked\r\n' + 'Connection: keep-alive\r\n' + '\r\n') + self.request.sendall(resp) + for x in xrange(0, parts): + self.request.sendall('{0}\r\n{1}\r\n'.format(len(str(x)), x)) + time.sleep(sleep_time) + if close: + self.request.sendall('0\r\n\r\n') + else: + self.request.sendall('lkfjasd;lfjas;d') + + time.sleep(2) + +class TestChunked(helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + + # create a socket server + cls.port = tsqa.utils.bind_unused_port()[1] + cls.server = tsqa.endpoint.SocketServerDaemon(ChunkedHandler, port=cls.port) + cls.server.start() + cls.server.ready.wait() + + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/'.format(cls.port)) + + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.http.connect_attempts_timeout': 5, + 'proxy.config.http.connect_attempts_max_retries': 0, + 'proxy.config.http.keep_alive_enabled_in': 1, + 'proxy.config.http.keep_alive_enabled_out': 1, + 'proxy.config.exec_thread.limit': 1, + 'proxy.config.exec_thread.autoconfig': 0, + 'proxy.config.http.chunking_enabled': 1, + }) + + def test_chunked_origin(self): + ''' + Test that the origin does in fact support keepalive + ''' + self._client_test_chunked_keepalive(self.port) + self._client_test_chunked_keepalive(self.port, num_bytes=2) + self._client_test_chunked_keepalive(self.port, num_bytes=2, sleep=1) + + def _client_test_chunked_keepalive(self, + port=None, + times=3, + num_bytes=None, + sleep=None, + ): + if port is None: + port = int(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('127.0.0.1', port)) + + url = '/' + if num_bytes is not None: + url += str(num_bytes) + if sleep is not None: + if num_bytes is None: + raise Exception() + url += '/' + str(sleep) + + request = ('GET ' + url + ' HTTP/1.1\r\n' + 'Host: 127.0.0.1\r\n' + '\r\n') + uuid = None + # test basic + for x in xrange(1, times): + s.send(request) + resp = '' + while True: + response = s.recv(4096) + for line in response.splitlines(): + line = line.strip() + if line.startswith('X-Conn-Id:'): + r_uuid = line.replace('X-Conn-Id:', '') + if uuid is None: + uuid = r_uuid + else: + self.assertEqual(uuid, r_uuid) + resp += response + if resp.endswith('\r\n0\r\n\r\n'): + break + for x in xrange(0, num_bytes or 4): + self.assertIn('1\r\n{0}\r\n'.format(x), resp) + s.close() + + def test_chunked_basic(self): + url = 'http://127.0.0.1:{0}'.format(self.port) + ret = requests.get(url, proxies=self.proxies) + self.assertEqual(ret.status_code, 200) + self.assertEqual(ret.text.strip(), '01234') + + # TODO: fix keepalive with chunked responses + def test_chunked_keepalive_server(self): + url = 'http://127.0.0.1:{0}'.format(self.port) + ret = requests.get(url, proxies=self.proxies) + conn_id = ret.headers['x-conn-id'] + self.assertEqual(ret.status_code, 200) + self.assertEqual(ret.text.strip(), '01234') + + # make sure that a second request works, and since we have keep-alive out + # disabled it should be a new connection + ret = requests.get(url, proxies=self.proxies) + self.assertEqual(ret.status_code, 200) + self.assertEqual(ret.text.strip(), '01234') + self.assertEqual(conn_id, ret.headers['x-conn-id']) + + def test_chunked_keepalive_client(self): + self._client_test_chunked_keepalive() + self._client_test_chunked_keepalive(num_bytes=2) + self._client_test_chunked_keepalive(num_bytes=2, sleep=1) + + def test_chunked_bad_close(self): + url = 'http://127.0.0.1:{0}/5/0.1/false'.format(self.port) + with self.assertRaises(requests.exceptions.ConnectionError): + ret = requests.get(url, proxies=self.proxies, timeout=2) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_connect_attempts.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_connect_attempts.py b/ci/tsqa/tests/test_connect_attempts.py new file mode 100644 index 0000000..9979e33 --- /dev/null +++ b/ci/tsqa/tests/test_connect_attempts.py @@ -0,0 +1,209 @@ +''' +Test Origin Server Connect Attempts +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import requests +import time +import logging +import socket +import struct +import select +import threading + +import helpers + +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +log = logging.getLogger(__name__) + + +def thread_die_on_connect(sock): + sock.listen(0) + # poll + read_sock = select.select([sock], [], []) + # exit + sock.close() + +def thread_delayed_accept_after_connect(sock): + ''' + Thread to sleep a decreasing amount of time before requests + + sleep times: 2 -> 1 -> 0 + ''' + sock.listen(0) + sleep_time = 2 + requests = 0 + # poll + while True: + read_sock = select.select([sock], [], []) + time.sleep(sleep_time) + try: + connection, addr = sock.accept() + connection.send(('HTTP/1.1 200 OK\r\n' + 'Content-Length: {body_len}\r\n' + 'Content-Type: text/html; charset=UTF-8\r\n' + 'Connection: close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests))) + connection.close() + requests += 1 + except Exception as e: + print 'connection died!', e + pass + if sleep_time > 0: + sleep_time -= 1 + + +def thread_reset_after_accept(sock): + sock.listen(0) + first = True + requests = 0 + while True: + connection, addr = sock.accept() + requests += 1 + if first: + first = False + connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) + connection.close() + else: + connection.send(('HTTP/1.1 200 OK\r\n' + 'Content-Length: {body_len}\r\n' + 'Content-Type: text/html; charset=UTF-8\r\n' + 'Connection: close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests))) + connection.close() + +def thread_partial_response(sock): + sock.listen(0) + first = True + requests = 0 + while True: + connection, addr = sock.accept() + requests += 1 + if first: + connection.send('HTTP/1.1 200 OK\r\n') + connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) + connection.close() + first = False + else: + connection.send(('HTTP/1.1 200 OK\r\n' + 'Content-Length: {body_len}\r\n' + 'Content-Type: text/html; charset=UTF-8\r\n' + 'Connection: close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests))) + connection.close() + + + +class TestOriginServerConnectAttempts(helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + cls.sock_map = {} + def _add_sock(name): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', 0)) + cls.sock_map[name] = sock.getsockname()[1] + cls.configs['remap.config'].add_line('map /{0}/ http://127.0.0.1:{1}/'.format(name, cls.sock_map[name])) + return sock + # create a socket where we just bind + _add_sock('bound') + + # create a socket where we bind + listen + sock = _add_sock('listen') + sock.listen(1) + + # create a bunch of special socket servers + sock = _add_sock('die_on_connect') + t = threading.Thread(target=thread_die_on_connect, args=(sock,)) + t.daemon = True + t.start() + + sock = _add_sock('reset_after_accept') + t = threading.Thread(target=thread_reset_after_accept, args=(sock,)) + t.daemon = True + t.start() + + sock = _add_sock('delayed_accept_after_connect') + t = threading.Thread(target=thread_delayed_accept_after_connect, args=(sock,)) + t.daemon = True + t.start() + + sock = _add_sock('partial_response') + t = threading.Thread(target=thread_partial_response, args=(sock,)) + t.daemon = True + t.start() + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + + # enable re-connects, timeout of 1s, max retires of 3 + cls.configs['records.config']['CONFIG']['proxy.config.http.connect_attempts_timeout'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.http.connect_attempts_max_retries'] = 3 + + def test_bound_origin(self): + '''Verify that we get 502s from an origin which just did a bind''' + url = 'http://127.0.0.1:{0}/bound/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 502) + + def test_listen_origin(self): + '''Verify that we get 502s from origins that bind + listen''' + url = 'http://127.0.0.1:{0}/listen/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 502) + + url = 'http://127.0.0.1:{0}/listen/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 502) + + def test_die_on_connect_origin(self): + '''Verify that we get 504s from origins that die_on_connect''' + url = 'http://127.0.0.1:{0}/die_on_connect/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 504) + + # TODO: FIX THIS!!! The test is correct, ATS isn't! + # we should fail in this case-- or at least have a config which lets you control + def test_partial_response_origin(self): + ''' + Verify that we get 504s from origins that return a partial_response + + We want to bail out-- since the origin already got the request, we can't + gaurantee that the request is re-entrant + ''' + url = 'http://127.0.0.1:{0}/partial_response/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 502) + + def test_reset_after_accept_origin(self): + '''Verify that we get 200s from origins that reset_after_accept''' + url = 'http://127.0.0.1:{0}/reset_after_accept/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + self.assertEqual(ret.status_code, 200) + self.assertGreater(int(ret.text), 0) + + def test_delayed_accept_after_connect_origin(self): + '''Verify that we get 200s from origins that delayed_accept_after_connect''' + url = 'http://127.0.0.1:{0}/delayed_accept_after_connect/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url) + # make sure it worked + self.assertEqual(ret.status_code, 200) + # make sure its not the first one (otherwise the test messed up somehow) + self.assertGreater(int(ret.text), 0) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_example.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_example.py b/ci/tsqa/tests/test_example.py new file mode 100644 index 0000000..0a7f468 --- /dev/null +++ b/ci/tsqa/tests/test_example.py @@ -0,0 +1,234 @@ +''' +Some example tests of the new tsqa +''' + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import requests +import time +import subprocess + +import helpers + +import tsqa.test_cases +import tsqa.utils + +# TODO: for some reason subclasses of subclasses of TestCase don't work with the +# decorator +#@helpers.unittest.skip('Not running TestNoOp, as it is a NoOp test') +class TestNoOp(helpers.EnvironmentCase): + ''' + This is purely a documentation test + ''' + # you can set configure/environment options for the source build here + environment_factory = { + 'configure': {# A value of None means that the argument has no value + 'enable-spdy': None, + # if there is a value it will be converted to --key=value + 'with-max-api-stats': 2048, + }, + 'env': None, + } + + @classmethod + def setUpClass(cls): + ''' + If you'd like to skip an entire test + ''' + # you can also skip (or conditionally skip) tests + raise helpers.unittest.SkipTest('Skip the entire class') + + @classmethod + def setUpEnv(cls, env): + ''' + This funciton is responsible for setting up the environment for this fixture + This includes everything pre-daemon start. + + You are passed in cls (which is the instance of this class) and env (which + is an environment object) + ''' + # we can modify any/all configs (note: all pre-daemon start) + cls.configs['remap.config'].add_line('map / http://http://trafficserver.readthedocs.org/') + + # Some configs have nicer wrapper objects to give you a more pythonic interface + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.log.squid_log_enabled': 1, + 'proxy.config.log.squid_log_is_ascii': 1, + }) + + def test_something(self): + ''' + All functions beginning with "test_" will be run as tests for the class. + Within these functions your environment is already set up and started-- + you only need to excercise the code that you intend to test + ''' + # for example, you could send a request to ATS and check the response + ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])) + + self.assertEqual(ret.status_code, 404) + self.assertIn('ATS', ret.headers['server']) + + +class TestConfigureFlags(helpers.EnvironmentCase): + environment_factory = { + 'configure': {'enable-spdy': None}, + } + + def test_spdy(self): + self.assertTrue(True) + + +class TestBootstrap(helpers.EnvironmentCase): + def test_default_404(self): + ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])) + + self.assertEqual(ret.status_code, 404) + self.assertIn('ATS', ret.headers['server']) + + def test_trafficline(self): + ''' + Test that traffic_line works, and verify that the values for proxy.config + match what we put in records.config + ''' + cmd = [os.path.join(self.environment.layout.bindir, 'traffic_line'), + '-m', + 'proxy.config', + ] + stdout, _ = tsqa.utils.run_sync_command(cmd, stdout=subprocess.PIPE) + for line in stdout.splitlines(): + if not line.strip(): + continue + k, v = line.split(' ', 1) + if k not in self.configs['records.config']['CONFIG']: + continue + r_val = self.configs['records.config']['CONFIG'][k] + self.assertEqual(type(r_val)(v), self.configs['records.config']['CONFIG'][k]) + + +class TestServerIntercept(helpers.EnvironmentCase, tsqa.test_cases.DynamicHTTPEndpointCase): + endpoint_port = 60000 + @classmethod + def setUpEnv(cls, env): + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}'.format(cls.endpoint_port)) + + cls.configs['plugin.config'].add_line('intercept.so') + + def hello(request): + return 'hello' + cls.http_endpoint.add_handler('/', hello) + + + def test_basic_intercept(self): + for _ in xrange(0, 10): + ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])) + + self.assertEqual(ret.status_code, 200) + + +class TestLogs(helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This funciton is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.tags': 'log-.*', + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.log.hostname': 'test', + 'proxy.config.log.search_top_sites': 1, + }) + def test_logs_exist(self): + # send some requests + for x in xrange(0, 10): + ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])) + + self.assertEqual(ret.status_code, 404) + self.assertIn('ATS', ret.headers['server']) + + # TODO: some better way to know when the logs where syncd + time.sleep(10) # wait for logs to hit disk + + # verify that the log files exist + for logfile in ('diags.log', 'error.log', 'squid.blog', 'traffic.out', 'manager.log'): + logfile_path = os.path.join(self.environment.layout.logdir, logfile) + self.assertTrue(os.path.isfile(logfile_path), logfile_path) + + +class TestLogRefCounting(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This funciton is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/\n'.format(cls.http_endpoint.address[1])) + + cls.configs['plugin.config'].add_lines([ + 'tcpinfo.so --log-file=tcpinfo1 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2', + 'tcpinfo.so --log-file=tcpinfo2 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2', + 'tcpinfo.so --log-file=tcpinfo3 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2', + 'tcpinfo.so --log-file=tcpinfo4 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2', + ]) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.log.max_space_mb_for_logs': 10, + 'proxy.config.log.max_space_mb_for_orphan_logs': 10, + 'proxy.config.log.squid_log_enabled': 1, + 'proxy.config.log.squid_log_is_ascii': 1, + 'proxy.config.log.rolling_interval_sec': 60, + 'proxy.config.log.rolling_size_mb': 1, + 'proxy.config.log.max_space_mb_headroom': 1, + 'proxy.config.log.max_secs_per_buffer': 1, + }) + + def test_logs_exist(self): + # send some requests + for x in xrange(0, 10): + ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])) + + self.assertEqual(ret.status_code, 404) + self.assertIn('ATS', ret.headers['server']) + + # TODO: some better way to know when the logs where syncd + time.sleep(10) # wait for logs to hit disk + + logfile_path = os.path.join(self.environment.layout.logdir, 'squid.log') + self.assertTrue(os.path.isfile(logfile_path), logfile_path) + + +class TestDynamicHTTPEndpointCase(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This funciton is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/\n'.format(cls.http_endpoint.address[1])) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + + def test_basic_proxy(self): + ret = requests.get(self.endpoint_url('/test'), + proxies=self.proxies, + ) + self.assertEqual(ret.status_code, 404) + self.assertIn('WSGIServer', ret.headers['server']) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_hostdb.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_hostdb.py b/ci/tsqa/tests/test_hostdb.py new file mode 100644 index 0000000..46bec6a --- /dev/null +++ b/ci/tsqa/tests/test_hostdb.py @@ -0,0 +1,86 @@ +''' +Test hostdb +''' + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import requests +import time + +import helpers + +import tsqa.test_cases + + +class TestHostDBPartiallyFailedDNS(helpers.EnvironmentCase): + ''' + Tests for how hostdb handles when there is one failed and one working resolver + ''' + @classmethod + def setUpEnv(cls, env): + resolv_conf_path = os.path.join(env.layout.prefix, 'resolv.conf') + + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.http.response_server_enabled': 2, # only add server headers when there weren't any + 'proxy.config.hostdb.lookup_timeout': 1, + 'proxy.config.dns.resolv_conf': resolv_conf_path, + 'proxy.config.url_remap.remap_required': 0, + + }) + + with open(resolv_conf_path, 'w') as fh: + fh.write('nameserver 1.1.1.0\n') # some non-existant nameserver + fh.write('nameserver 8.8.8.8\n') # some REAL nameserver + + def test_working(self): + start = time.time() + ret = requests.get('http://trafficserver.readthedocs.org', + proxies=self.proxies, + ) + self.assertLess(time.time() - start, self.configs['records.config']['CONFIG']['proxy.config.hostdb.lookup_timeout']) + self.assertEqual(ret.status_code, 200) + + +class TestHostDBFailedDNS(helpers.EnvironmentCase): + ''' + Tests for how hostdb handles when there is no reachable resolver + ''' + @classmethod + def setUpEnv(cls, env): + resolv_conf_path = os.path.join(env.layout.prefix, 'resolv.conf') + + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.http.response_server_enabled': 2, # only add server headers when there weren't any + 'proxy.config.hostdb.lookup_timeout': 1, + 'proxy.config.dns.resolv_conf': resolv_conf_path, + 'proxy.config.url_remap.remap_required': 0, + + }) + + with open(resolv_conf_path, 'w') as fh: + fh.write('nameserver 1.1.1.0\n') # some non-existant nameserver + + def test_lookup_timeout(self): + start = time.time() + ret = requests.get('http://some_nonexistant_domain', + proxies=self.proxies, + ) + self.assertGreater(time.time() - start, self.configs['records.config']['CONFIG']['proxy.config.hostdb.lookup_timeout']) + self.assertEqual(ret.status_code, 502) + self.assertIn('ATS', ret.headers['server']) + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_https.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_https.py b/ci/tsqa/tests/test_https.py new file mode 100644 index 0000000..a8914e6 --- /dev/null +++ b/ci/tsqa/tests/test_https.py @@ -0,0 +1,273 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from OpenSSL import SSL +import socket + +import helpers +import tsqa.utils + +# some ciphers to test with +CIPHER_MAP = { + 'rsa': 'ECDHE-RSA-AES256-GCM-SHA384', + 'ecdsa': 'ECDHE-ECDSA-AES256-GCM-SHA384', +} + + +class CertSelectionMixin(object): + def _get_cert(self, addr, sni_name=None, ciphers=None): + ''' + Return the certificate for addr. Optionally sending sni_name + ''' + ctx = SSL.Context(SSL.TLSv1_2_METHOD) + # Set up client + sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sock.connect(addr) + if sni_name is not None: + sock.set_tlsext_host_name(sni_name) + if ciphers is not None: + ctx.set_cipher_list(ciphers) + sock.do_handshake() + return sock.get_peer_certificate() + + def _get_cert_chain(self, addr, sni_name=None, ciphers=None): + ''' + Return the certificate chain for addr. Optionally sending sni_name + ''' + ctx = SSL.Context(SSL.TLSv1_2_METHOD) + # Set up client + sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sock.connect(addr) + if sni_name is not None: + sock.set_tlsext_host_name(sni_name) + if ciphers is not None: + ctx.set_cipher_list(ciphers) + sock.do_handshake() + return sock.get_peer_cert_chain() + + def test_star_ordering(self): + ''' + We should be served the first match, since we aren't sending SNI headers + ''' + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_star_sni(self): + ''' + Make sure we get the certificate we asked for if we pass in SNI headers + ''' + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, sni_name='www.test.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.test.com') + + cert = self._get_cert(addr, sni_name='www.example.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ip_ordering(self): + ''' + We should be served the first match, since we aren't sending SNI headers + ''' + addr = ('127.0.0.2', self.ssl_port) + cert = self._get_cert(addr) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ip_sni(self): + ''' + Make sure we get the certificate we asked for if we pass in SNI headers + ''' + addr = ('127.0.0.2', self.ssl_port) + cert = self._get_cert(addr, sni_name='www.test.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.test.com') + + cert = self._get_cert(addr, sni_name='www.example.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def _intermediate_ca_t(self, cipher): + ''' + Method for testing intermediate CAs. We assume that www.example.com should + return a certificate chaing of len 2 which includes intermediate. + We also assume that www.test.com returns a single cert in the chain which + is *not* intermediate + ''' + # send a request that *should* get an intermediate CA + addr = ('127.0.0.1', self.ssl_port) + cert_chain = self._get_cert_chain(addr, ciphers=CIPHER_MAP[cipher]) + self.assertEqual(len(cert_chain), 2) + self.assertEqual(cert_chain[0].get_subject().commonName.decode(), 'www.example.com') + self.assertEqual(cert_chain[1].get_subject().commonName.decode(), 'intermediate') + + # send a request that shouldn't get an intermediate CA + addr = ('127.0.0.1', self.ssl_port) + cert_chain = self._get_cert_chain(addr, ciphers=CIPHER_MAP[cipher], sni_name='www.test.com') + self.assertEqual(len(cert_chain), 1) + self.assertEqual(cert_chain[0].get_subject().commonName.decode(), 'www.test.com') + + +class TestRSA(helpers.EnvironmentCase, CertSelectionMixin): + ''' + Tests for https for ATS configured with RSA certificates + ''' + @classmethod + def setUpEnv(cls, env): + # add an SSL port to ATS + cls.ssl_port = tsqa.utils.bind_unused_port()[1] + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl'.format(cls.ssl_port) + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl', + 'proxy.config.ssl.server.cipher_suite': CIPHER_MAP['rsa'], + }) + + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0} ssl_ca_name={1}'.format( + helpers.tests_file_path('rsa_keys/www.example.com.pem'), + helpers.tests_file_path('rsa_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0}'.format( + helpers.tests_file_path('rsa_keys/www.test.com.pem'), + )) + + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0} ssl_ca_name={1}'.format( + helpers.tests_file_path('rsa_keys/www.example.com.pem'), + helpers.tests_file_path('rsa_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0}'.format( + helpers.tests_file_path('rsa_keys/www.test.com.pem'), + )) + + def test_rsa(self): + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, ciphers=CIPHER_MAP['rsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ecdsa(self): + addr = ('127.0.0.1', self.ssl_port) + with self.assertRaises(Exception): + cert = self._get_cert(addr, ciphers=CIPHER_MAP['ecdsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_intermediate_ca_rsa(self): + self._intermediate_ca_t('rsa') + + def test_intermediate_ca_ecdsa(self): + with self.assertRaises(Exception): + self._intermediate_ca_t('ecdsa') + +class TestECDSA(helpers.EnvironmentCase, CertSelectionMixin): + ''' + Tests for https for ATS configured with ECDSA certificates + ''' + @classmethod + def setUpEnv(cls, env): + # add an SSL port to ATS + cls.ssl_port = tsqa.utils.bind_unused_port()[1] + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl'.format(cls.ssl_port) + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl', + 'proxy.config.ssl.server.cipher_suite': CIPHER_MAP['ecdsa'], + }) + + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0} ssl_ca_name={1}'.format( + helpers.tests_file_path('ec_keys/www.example.com.pem'), + helpers.tests_file_path('ec_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0}'.format( + helpers.tests_file_path('ec_keys/www.test.com.pem'), + )) + + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0} ssl_ca_name={1}'.format( + helpers.tests_file_path('ec_keys/www.example.com.pem'), + helpers.tests_file_path('ec_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0}'.format( + helpers.tests_file_path('ec_keys/www.test.com.pem'), + )) + + def test_rsa(self): + addr = ('127.0.0.1', self.ssl_port) + with self.assertRaises(Exception): + cert = self._get_cert(addr, ciphers=CIPHER_MAP['rsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ecdsa(self): + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, ciphers=CIPHER_MAP['ecdsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_intermediate_ca_rsa(self): + with self.assertRaises(Exception): + self._intermediate_ca_t('rsa') + + def test_intermediate_ca_ecdsa(self): + self._intermediate_ca_t('ecdsa') + +class TestMix(helpers.EnvironmentCase, CertSelectionMixin): + ''' + Tests for https for ATS configured with both ECDSA and RSA certificates + ''' + @classmethod + def setUpEnv(cls, env): + # add an SSL port to ATS + cls.ssl_port = tsqa.utils.bind_unused_port()[1] + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl'.format(cls.ssl_port) + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl', + 'proxy.config.ssl.server.cipher_suite': '{0}:{1}'.format(CIPHER_MAP['ecdsa'], CIPHER_MAP['rsa']), + }) + + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0},{1} ssl_ca_name={2},{3}'.format( + helpers.tests_file_path('rsa_keys/www.example.com.pem'), + helpers.tests_file_path('ec_keys/www.example.com.pem'), + helpers.tests_file_path('rsa_keys/intermediate.crt'), + helpers.tests_file_path('ec_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0},{1}'.format( + helpers.tests_file_path('rsa_keys/www.test.com.pem'), + helpers.tests_file_path('ec_keys/www.test.com.pem'), + )) + + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0},{1} ssl_ca_name={2},{3}'.format( + helpers.tests_file_path('rsa_keys/www.example.com.pem'), + helpers.tests_file_path('ec_keys/www.example.com.pem'), + helpers.tests_file_path('rsa_keys/intermediate.crt'), + helpers.tests_file_path('ec_keys/intermediate.crt'), + )) + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0},{1}'.format( + helpers.tests_file_path('rsa_keys/www.test.com.pem'), + helpers.tests_file_path('ec_keys/www.test.com.pem'), + )) + + def test_rsa(self): + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, ciphers=CIPHER_MAP['rsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ecdsa(self): + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, ciphers=CIPHER_MAP['ecdsa']) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_intermediate_ca_rsa(self): + self._intermediate_ca_t('rsa') + + def test_intermediate_ca_ecdsa(self): + self._intermediate_ca_t('ecdsa')