summaryrefslogtreecommitdiff
path: root/usr/src/test/smartos-test/smartos-test.sh
blob: b1c4d8cfbffedca4355f2c3a26504a38c8c21bd4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#! /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 2020 Joyent, Inc.
# Copyright 2022 MNX Cloud, 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 <<EOF
To setup and run these tests you must create the file:
    /lib/sdc/.sdc-test-no-production-data
after ensuring you have no production data on this system.
EOF
        exit 1
    fi
}

function zone_check {
    if [[ $(zonename) != "global" ]]; then
        fatal "these tests must be run from the global zone."
    fi
}

#
# Check that the tests.buildstamp file in the test archive matches
# the current platform stamp. Running tests designed for a platform
# that we're not running is a bad idea.
#
function version_check {
    PLATFORM_VERSION=$(uname -v | sed -e 's/^joyent_//g')
    mkdir -p /tmp/version_check.$$
    tar xzf $1 -C /tmp/version_check.$$ ./tests.buildstamp
    TESTS_VERSION=$(cat /tmp/version_check.$$/tests.buildstamp)
    rm -rf /tmp/version_check.$$
    log "Platform version: $PLATFORM_VERSION"
    log "   Tests version: $TESTS_VERSION"
    if [[ "$PLATFORM_VERSION" != "$TESTS_VERSION" ]]; then
        fatal "mismatched platform version and tests version!"
    fi
}

function snapshot_rollback_opt {
    snapshot="system-test-smartos-test"
    has_snapshot=$(zfs list zones/opt@$snapshot 2> /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
        if ! grep -q '^cyrus:' /etc/shadow; then
            echo "cyrus:*LK*:::::::" >> /etc/shadow
        fi
        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
        if ! grep -q '^ztest:' /etc/shadow; then
            # For sudo to work, the ztest account must not be locked
            echo "ztest:NP:::::::" >> /etc/shadow
        fi
        mkdir -p /zones/global/ztest
        chown ztest /zones/global/ztest
        zprofile=/zones/global/ztest/.profile
        if [[ ! -f $zprofile ]]; then
            cat > $zprofile <<-EOF
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/tools/bin:/opt/tools/sbin:/opt/zfs-tests/bin
export PATH

KEEP="zones"
export KEEP
EOF

            if [[ -n "$DISKS" ]]; then
                echo "DISKS=\"$DISKS\"" >> $zprofile
		echo "export DISKS" >> $zprofile
            else
                msg="echo Please set \$DISKS appropriate before running zfstest"
                echo $msg >> $zprofile
            fi

            chown ztest $zprofile
        fi
    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/bhyve-tests/bin/bhyvetest
    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] <path to tests.tgz>"
    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