#! /usr/bin/bash # # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2019 Joyent, Inc. # # # This script is designed to run on an (effectively) disposable SmartOS # install to configure the system, install a series of tests from the # smartos-gate, and execute them. # It exits 1 if any configuration, setup or test fails. # export PATH=/usr/bin:/usr/sbin:/opt/tools/sbin:/opt/tools/bin:$PATH # The pkgsrc packages we will install. export SMARTOS_TEST_PKGS=" python27 sudo coreutils gcc7 gmake " # # Set $KEEP as a precaution in case we ever end up running the zfs-test suite # by accident or design. This ensures it never attempts to destroy the 'zones' # zpool. Note that the ZFS test suite also wants DISKS set to the disks which # it can create/destroy pools on, but we're not computing that here. # if [[ -z "$KEEP" ]]; then export KEEP="zones" fi # # Accumulate test suite exit codes and a list of failed tests # RESULT=0 FAILED_TESTS="" function fatal { echo "ERROR: $@" exit 1 } function warn { echo "WARNING: $@" } function log { echo "$@" } function log_must { echo "Running $@" $@ || fatal "Error running command." } function log_test { echo "" TEST_NAME=$1 shift echo "Starting test for $TEST_NAME with $@" $@ TEST_RESULT=$? if [[ $TEST_RESULT -ne 0 ]]; then FAILED_TESTS="$FAILED_TESTS $TEST_NAME" fi RESULT=$(( $RESULT + $TEST_RESULT )) } function log_testrunner { echo "" TEST_NAME=$1 shift echo "Starting test-runner for $TEST_NAME with $@" /opt/test-runner/bin/run -c $@ TEST_RESULT=$? if [[ $TEST_RESULT -ne 0 ]]; then FAILED_TESTS="$FAILED_TESTS $TEST_NAME" fi RESULT=$(( $RESULT + $TEST_RESULT )) # test-runner's default log dirs use a timestamp at per-second granularity. # Sleep here to ensure a unique timestamp per run if consecutive tests # bail out early. sleep 1 } function guard_production_data { if [[ ! -f "/lib/sdc/.sdc-test-no-production-data" ]]; then cat < /dev/null) if [[ -n "$has_snapshot" ]]; then log_must zfs rollback zones/opt@$snapshot else log_must zfs snapshot zones/opt@$snapshot fi } # # Since some tests want to deliver to /usr which is read-only on SmartOS, # we make a temporary directory, dump the current /usr there, extract our # content to it, then lofs-mount it over the real thing. # function add_loopback_mounts { test_archive=$1 lofs_home=/var/tmp/smartos-test-loopback # If /usr is already lofs mounted, and pointing at $lofs_home, just # extract our new test bits on top. Ideally we'd just unmount it, # but while running this script, there's a good chance that the dataset # will be busy and the umount would fail. FS=$(/bin/df -n /usr | awk '{print $NF'}) if [[ "$FS" == "lofs" ]]; then is_test_lofs=$(mount | grep ^/usr | grep "$lofs_home/usr ") if [[ -z "$is_test_lofs" ]]; then fatal "unsupported: existing lofs mount for /usr is not $lofs_home" else log "Extracting new test archive to lofs-mounted /usr" # extract the current test archive to it log_must tar -xzf $test_archive -C $lofs_home ./usr fi # Otherwise, setup a lofs mount for it. else log "Creating new lofs mount for /usr on $lofs_home" rm -rf $lofs_home mkdir -p $lofs_home find /usr | cpio -pdum $lofs_home log_must tar -xzf $test_archive -C $lofs_home ./usr # keep /usr read-only in an attempt to preserve smartos behaviour # unless specifically asked to if [[ "$mount_usr_rw" = "true" ]]; then mount_opts="-o rw" else mount_opts="-o ro" fi log_must mount -O -F lofs $mount_opts $lofs_home/usr /usr fi } # # Extract the non-/usr parts of the test archive # function extract_remaining_test_bits { log_must tar -xzf $1 -C / \ ./opt ./kernel ./tests.manifest.gen ./tests.buildstamp } function setup_pkgsrc { if [[ -f /opt/tools/etc/pkgin/repositories.conf ]]; then log "Pkgsrc bootstrap already setup, continuing" return fi # We should always use the same pkgsrc version as we have installed # on the build machine in case any of our tests link against libraries # in /opt/local PKGSRC_STEM="https://pkgsrc.joyent.com/packages/SmartOS/bootstrap" BOOTSTRAP_TAR="bootstrap-2018Q4-tools.tar.gz" BOOTSTRAP_SHA="b599667c80e4a42157763ed25d868ec7dc34962d" # Ensure we are in a directory with enough space for the bootstrap # download, by default the SmartOS /root directory is limited to the size # of the ramdisk. cd /var/tmp # Download the bootstrap kit to the current directory. Note that we # currently pass "-k" to skip SSL certificate checks as the GZ doesn't # install them. log_must curl -kO ${PKGSRC_STEM}/${BOOTSTRAP_TAR} # Verify the SHA1 checksum. [[ "${BOOTSTRAP_SHA}" = "$(/bin/digest -a sha1 ${BOOTSTRAP_TAR})" ]] || \ fatal "checksum failure for ${BOOTSTRAP_TAR}, expected ${BOOTSTRAP_SHA}" # Install bootstrap kit to /opt/tools log_must tar -zxpf ${BOOTSTRAP_TAR} -C / # add a symlink from /opt/local, needed by many test suites if [[ ! -d /opt/local && ! -L /opt/local ]]; then log_must ln -s /opt/tools /opt/local else log "Not forging /opt/local link" fi } function install_required_pkgs { log_must pkgin -y in ${SMARTOS_TEST_PKGS} } function add_test_accounts { grep -q cyrus: /etc/passwd if [[ $? -ne 0 ]]; then log "Adding cyrus user" echo "cyrus:x:977:1::/zones/global/cyrus:/bin/sh" >> /etc/passwd echo "cyrus:*LK*:::::::" >> /etc/shadow mkdir -p /zones/global/cyrus chown cyrus /zones/global/cyrus fi grep -q ztest: /etc/passwd if [[ $? -ne 0 ]]; then log "Adding ztest user" echo "ztest:x:978:1::/zones/global/ztest:/bin/sh" >> /etc/passwd echo "ztest:*LK*:::::::" >> /etc/shadow mkdir -p /zones/global/ztest chown ztest /zones/global/ztest fi if [[ ! -f /opt/tools/etc/sudoers.d/ztest ]]; then mkdir -p /opt/tools/etc/sudoers.d echo "ztest ALL=(ALL) NOPASSWD: ALL" >> /opt/tools/etc/sudoers.d/ztest fi } # # By using log_test or log_testrunner, we accumulate the exit codes from each # test run to $RESULT. # # We don't - yet - run net-tests, smbclient-tests, zfs-tests, or the dtrace # suite. # function execute_tests { log "Starting test runs" log_test bhyvetest /opt/bhyvetest/bin/bhyvetest -ak log_testrunner crypto-tests /opt/crypto-tests/runfiles/default.run log_testrunner elf-tests /opt/elf-tests/runfiles/default.run log_testrunner libc-tests /opt/libc-tests/runfiles/default.run log_test vndtest /opt/vndtest/bin/vndtest -a log_testrunner util-tests /opt/util-tests/runfiles/default.run log_testrunner os-tests /opt/os-tests/runfiles/default.run if [[ -n "$FAILED_TESTS" ]]; then echo "" log "Failures were seen in the following test suites: $FAILED_TESTS" fi } function usage { echo "Usage: smartos-test [-h] [-c] [-e] [-r] [-w] " echo "" echo "At least one of -c, -e, -r is required." echo "" echo " -h print usage" echo " -c configure the system for testing" echo " -e execute known tests" echo " -f skip the check to ensure platform version == test version" echo " -r snapshot or rollback to zones/opt@system-test-smartos-test" echo " before doing any system configuration or test execution" echo " -w when mounting the lofs /usr, make it writable" } mount_usr_rw=false skip_version_check=false do_configure=false do_execute=false do_rollback=false # # Main # while getopts "cefrwh" opt; do case "${opt}" in c) do_configure=true ;; e) do_execute=true ;; f) skip_version_check=true ;; r) do_rollback=true ;; h) usage exit 2 ;; w) mount_usr_rw=true ;; *) log "unknown argument ${opt}" usage exit 2 esac done shift $((OPTIND - 1)) test_archive=$1 if [[ -z "$test_archive" ]]; then log "missing test archive argument." usage exit 1 fi if [[ ! -f "$test_archive" ]]; then usage fatal "unable to access test archive at $test_archive" fi if [[ "$do_rollback" = false && \ "$do_configure" = false && \ "$do_execute" = false ]]; then log "nothing to do: use at least one of -r -e -c" usage exit 2 fi if [[ "$skip_version_check" = false ]]; then version_check $1 fi guard_production_data zone_check if [[ $do_rollback = true ]]; then snapshot_rollback_opt fi if [[ $do_configure = true ]]; then add_loopback_mounts $test_archive extract_remaining_test_bits $test_archive add_test_accounts setup_pkgsrc install_required_pkgs log "This system is now configured to run the SmartOS tests." fi if [[ "$do_execute" = true ]]; then execute_tests fi if [[ $RESULT -gt 0 ]]; then exit 1 else exit 0 fi