#!/bin/sh # # This script (along with the .travis.yml file) is used by Travis CI to # build Bro and run the tests. # # This script can also be used outside of Travis (the "all" build step is # especially convenient in this case). Note that if you use this script # outside of Travis then you will need to fetch the private tests manually # (if you don't, then the private tests will be skipped). usage() { echo "usage: $0 CMD DISTRO" echo " CMD is a build step:" echo " install: install prereqs" echo " build: build bro" echo " run: run the tests" echo " all: do all of the above" echo " DISTRO is a Linux distro, 'travis' to run without docker, or 'coverity' to run a coverity scan" } if [ $# -ne 2 ]; then usage exit 1 fi step=$1 distro=$2 case $step in install) ;; build) ;; run) ;; all) ;; *) echo "Error: unknown build step: $step"; usage; exit 1 ;; esac # Install the coverity tools. install_coverity() { rm -rf coverity_tool.tgz coverity-tools cov-analysis* echo "Downloading coverity tools..." wget -nv https://scan.coverity.com/download/cxx/linux64 --post-data "token=${COV_TOKEN}&project=Bro" -O coverity_tool.tgz tar xzf coverity_tool.tgz rm coverity_tool.tgz mv cov-analysis* coverity-tools } # Build Bro with the coverity tools. build_coverity() { # Cleanup any previous build (this is really only necessary if running this # outside of Travis). make distclean > /dev/null ./configure --prefix=`pwd`/build/root --enable-debug --disable-perftools --disable-broker-tests --disable-python --disable-broctl export PATH=`pwd`/coverity-tools/bin:$PATH cd build cov-build --dir cov-int make -j 4 cd .. } # Create a tar file and send it to coverity. run_coverity() { EMAIL=bro-commits-internal@bro.org FILE=myproject.tgz VER=`cat VERSION` DESC=`git rev-parse HEAD` cd build echo "Creating tar file and sending to coverity..." tar czf ${FILE} cov-int curl --form token=${COV_TOKEN} --form email=${EMAIL} --form file=@${FILE} --form "version=${VER}" --form "description=${DESC}" https://scan.coverity.com/builds?project=Bro } # Create a docker container, and install all packages needed to build Bro. install_in_docker() { case $distro in centos_7) distro_cmds="yum -y install gdb cmake make gcc gcc-c++ flex bison libpcap-devel openssl-devel git openssl which" ;; debian_9) distro_cmds="apt-get update; apt-get -y install gdb cmake make gcc g++ flex bison python libpcap-dev libssl-dev zlib1g-dev libkrb5-dev git sqlite3 curl bsdmainutils" ;; fedora_28) distro_cmds="yum -y install gdb cmake make gcc gcc-c++ flex bison libpcap-devel openssl-devel git sqlite findutils which; ln -s /usr/bin/python3 /usr/local/bin/python" ;; ubuntu_16.04) distro_cmds="apt-get update; apt-get -y install gdb cmake make gcc g++ flex bison python libpcap-dev libssl-dev zlib1g-dev libkrb5-dev git sqlite3 curl bsdmainutils" ;; ubuntu_18.04) distro_cmds="apt-get update; apt-get -y install gdb cmake make gcc g++ flex bison python3 libpcap-dev libssl-dev zlib1g-dev libkrb5-dev git sqlite3 curl bsdmainutils; ln -s /usr/bin/python3 /usr/local/bin/python" ;; *) echo "Error: distro ${distro} is not recognized by this script" exit 1 ;; esac docker_image=`echo $distro | tr '_' ':'` docker run --name brotest -id -v "`pwd`:/bro" -w /bro ${docker_image} sh docker exec brotest sh -c "${distro_cmds}" } # Build bro in a docker container. build_in_docker() { docker exec brotest sh testing/scripts/travis-job build travis } # Run Bro tests in a docker container. run_in_docker() { prepare_env docker exec -t -e TRAVIS -e TRAVIS_PULL_REQUEST -e trav_key -e trav_iv brotest sh testing/scripts/travis-job run travis } # Build Bro. build() { # Cleanup any previous build (this is really only necessary if running this # outside of Travis). make distclean > /dev/null # Skip building broker tests, python bindings, and broctl, as these are # not needed by the bro tests. ./configure --build-type=Release --disable-broker-tests --disable-python --disable-broctl && make -j 2 } # Rename the encrypted environment variables to avoid having the hash value # hard-coded multiple times in this script. prepare_env() { if [ -z "$trav_key" ]; then # This hash value is found by logging into the Travis CI website, # and looking at the settings in the bro repo (look in the # "Environment Variables" section). hash=6a6fe747ff7b eval "trav_key=\$encrypted_${hash}_key" eval "trav_iv=\$encrypted_${hash}_iv" # Export so they are visible in docker containers. export trav_key export trav_iv fi } # Get the private tests. get_private_tests() { prepare_env if [ "${TRAVIS}" != "true" ]; then # When not running in the Travis environment, just skip trying to get # the private tests. echo "Note: skipping private tests (to run them, do a git clone of the private testing repo in the 'testing/external' directory before running this script)." elif [ -n "$trav_key" ] && [ -n "$trav_iv" ]; then curl https://www.bro.org/static/travis-ci/travis_key.enc -o travis_key.enc openssl aes-256-cbc -K $trav_key -iv $trav_iv -in travis_key.enc -out travis_key -d chmod 600 travis_key mkdir -p ~/.ssh mv travis_key ~/.ssh/id_rsa echo "git.bro.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmlu+EaJfPKTVqoEUzb5JBEdvNiFxO2wm7Vl61dGBl57avakFl8YnRujbA2yxlpC2xnEKD5y++hXxtxRLefyCM=" >> ~/.ssh/known_hosts git clone ssh://git@git.bro.org/bro-testing-private rm ~/.ssh/id_rsa elif [ -n "${TRAVIS_PULL_REQUEST}" ] && [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then # For pull request builds, the private key is not available, so skip # the private tests to avoid failing. echo "Note: skipping private tests because encrypted env. variables are not available in pull request builds." else echo "Error: cannot get private tests because encrypted env. variables are not defined." exit 1 fi } # Run Bro tests. run() { ulimit -c unlimited ulimit -a echo echo "Running unit tests ##################################################" echo cd testing/btest set +e # Must specify a value for "-j" option, otherwise Travis uses a huge value. ../../aux/btest/btest -j 4 -d ret=$? set -e echo echo "Getting external tests ##############################################" echo cd ../external if [ ! -d bro-testing ]; then make init fi if [ -d bro-testing ]; then commit=`cat commit-hash.bro-testing` echo "Checking out $commit" ( cd bro-testing && git checkout -q $commit ) fi echo if [ ! -d bro-testing-private ]; then get_private_tests fi if [ -d bro-testing-private ]; then commit=`cat commit-hash.bro-testing-private` echo "Checking out $commit" ( cd bro-testing-private && git checkout -q $commit ) fi echo echo "Running external tests ##############################################" echo set +e if [ -d bro-testing ]; then cd bro-testing make if [ $? -ne 0 ]; then showdiag ret=1 fi cd .. fi if [ -d bro-testing-private ]; then cd bro-testing-private make if [ $? -ne 0 ]; then showdiag ret=1 fi cd .. fi cd ../.. echo echo "Result code after running tests: $ret" if [ $ret -ne 0 ]; then COREFILES=`find testing/btest/.tmp testing/external/*/.tmp -type f -name core*` echo echo "Search for core dumps ##############################################" echo echo $COREFILES for cf in $COREFILES; do echo echo "############# Begin stack trace for $cf ###############" gdb build/src/bro -c "$cf" -ex "thread apply all bt" -ex "set pagination 0" -batch; echo "############# End stack trace for $cf #################" echo done fi # If we get here, then external tests were successful. exit $ret } # Show failed tests (not skipped tests) from diag.log when a test fails. showdiag() { f=diag.log grep -qs '... failed$' $f && \ echo && \ echo "Output of failed external tests #####################################" && \ echo && \ grep -v "... not available, skipped" $f } # Remove the docker container. remove_container() { echo "Removing the docker container..." docker rm -f brotest > /dev/null } if [ ! -f testing/scripts/travis-job ]; then echo "Error: must change directory to root of bro source tree before running this script." exit 1 fi set -e if [ "${TRAVIS_EVENT_TYPE}" = "cron" ]; then # This is a Travis CI cron job, so check the job number. # Extract second component of the job number. if [ -z "${TRAVIS_JOB_NUMBER}" ]; then echo "Error: TRAVIS_JOB_NUMBER is not defined (it should be defined by Travis CI)" exit 1 fi job=`echo ${TRAVIS_JOB_NUMBER} | cut -d . -f 2` # If this isn't the first job in a Travis CI build, then just output a # message and exit (this is not an error). if [ "$job" != "1" ]; then echo "Coverity scan is performed only in the first job of this build" exit 0 fi fi if [ "${TRAVIS_EVENT_TYPE}" = "cron" ] || [ "$distro" = "coverity" ]; then # Run coverity scan when this script is run from a Travis cron job, or # if the user specifies the "coverity" distro. # Check if the project token is available (this is a secret value and # should not be hard-coded in this script). This value can be found by # logging into the coverity scan web site and looking in the project # settings. if [ -z "${COV_TOKEN}" ]; then echo "Error: COV_TOKEN is not defined (should be defined in environment variables section of Travis settings for this repo)" exit 1 fi # The "build" and "run" steps are split up into separate steps because the # build outputs thousands of lines (which are conveniently collapsed into # a single line when viewing the "Job log" on the Travis CI web site). if [ "$step" = "install" ]; then install_coverity elif [ "$step" = "build" ]; then build_coverity elif [ "$step" = "run" ]; then run_coverity elif [ "$step" = "all" ]; then install_coverity build_coverity run_coverity fi elif [ "$distro" = "travis" ]; then # Build bro and run tests. # The "build" and "run" steps are split up into separate steps because the # build outputs thousands of lines (which are conveniently collapsed into # a single line when viewing the "Job log" on the Travis CI web site). if [ "$step" = "build" ]; then build elif [ "$step" = "run" ]; then run elif [ "$step" = "all" ]; then build run fi else # Build bro and run tests in a docker container. if [ "$step" = "install" ]; then install_in_docker elif [ "$step" = "build" ]; then build_in_docker elif [ "$step" = "run" ]; then run_in_docker elif [ "$step" = "all" ]; then install_in_docker build_in_docker run_in_docker # If all tests pass, then remove the docker container. remove_container fi fi