podman

Форк
0
/
test-compose 
429 строк · 11.8 Кб
1
#!/usr/bin/env bash
2
#
3
# Usage: test-compose [testname]
4
#
5
set -Eu
6
ME=$(basename $0)
7

8
###############################################################################
9
# BEGIN stuff you can but probably shouldn't customize
10

11
# Directory where this script and all subtests live
12
TEST_ROOTDIR=$(realpath $(dirname $0))
13

14
# Podman executable
15
PODMAN_BIN=$(realpath $TEST_ROOTDIR/../../bin)/podman
16

17
# Local path to docker socket with unix prefix
18
# The path will be changed for rootless users
19
DOCKER_SOCK=/var/run/docker.sock
20

21
# END   stuff you can but probably shouldn't customize
22
###############################################################################
23
# BEGIN setup
24

25
export TMPDIR=${TMPDIR:-/var/tmp}
26
WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX)
27

28
# Log of all HTTP requests and responses; always make '.log' point to latest
29
LOGBASE=${TMPDIR}/$ME.log
30
LOG=${LOGBASE}.$(date +'%Y%m%dT%H%M%S')
31
ln -sf $LOG $LOGBASE
32

33
# Keep track of test count and failures in files, not variables, because
34
# variables don't carry back up from subshells.
35
testcounter_file=$WORKDIR/.testcounter
36
failures_file=$WORKDIR/.failures
37

38
echo 0 >$testcounter_file
39
echo 0 >$failures_file
40

41
# END   setup
42
###############################################################################
43
# BEGIN infrastructure code - the helper functions used in tests themselves
44

45
#################
46
#  is_rootless  #  Check if we run as normal user
47
#################
48
function is_rootless() {
49
    [ "$(id -u)" -ne 0 ]
50
}
51

52
#########
53
#  die  #  Exit error with a message to stderr
54
#########
55
function die() {
56
    echo "$ME: $*" >&2
57
    exit 1
58
}
59

60
########
61
#  is  #  Simple comparison
62
########
63
function is() {
64
    local actual=$1
65
    local expect=$2
66
    local testname=$3
67

68
    if [[ "$actual" = "$expect" ]]; then
69
        # On success, include expected value; this helps readers understand
70
        _show_ok 1 "$testname=$expect"
71
        return
72
    fi
73
    _show_ok 0 "$testname" "$expect" "$actual"
74
}
75

76
##########
77
#  like  #  Compare, but allowing patterns
78
##########
79
function like() {
80
    local actual=$1
81
    local expect=$2
82
    local testname=$3
83

84
    # "is" (equality) is a subset of "like", but one that expr fails on if
85
    # the expected result has shell-special characters like '['. Treat it
86
    # as a special case.
87

88
    if [[ "$actual" = "$expect" ]]; then
89
        _show_ok 1 "$testname=$expect"
90
        return
91
    fi
92

93
    if expr "$actual" : ".*$expect" &>/dev/null; then
94
        # On success, include expected value; this helps readers understand
95
        _show_ok 1 "$testname ('$actual') ~ $expect"
96
        return
97
    fi
98
    _show_ok 0 "$testname" "~ $expect" "$actual"
99
}
100

101
##############
102
#  _show_ok  #  Helper for is() and like(): displays 'ok' or 'not ok'
103
##############
104
function _show_ok() {
105
    local ok=$1
106
    local testname=$2
107

108
    # If output is a tty, colorize pass/fail
109
    local red=
110
    local green=
111
    local reset=
112
    local bold=
113
    if [ -t 1 ]; then
114
        red='\e[31m'
115
        green='\e[32m'
116
        reset='\e[0m'
117
        bold='\e[1m'
118
    fi
119

120
    _bump $testcounter_file
121
    count=$(<$testcounter_file)
122

123
    # "skip" is a special case of "ok". Assume that our caller has included
124
    # the magical '# skip - reason" comment string.
125
    if [[ $ok == "skip" ]]; then
126
        # colon-plus: replace green with yellow, but only if green is non-null
127
        green="${green:+\e[33m}"
128
        ok=1
129
    fi
130
    if [ $ok -eq 1 ]; then
131
        echo -e "${green}ok $count $testname${reset}"
132
        echo    "ok $count $testname" >>$LOG
133
        return
134
    fi
135

136
    # Failed
137
    local expect=$3
138
    local actual=$4
139
    printf "${red}not ok $count $testname${reset}\n"
140
    # Not all errors include actual/expect
141
    if [[ -n "$expect" || -n "$actual" ]]; then
142
        printf "${red}#  expected: %s${reset}\n" "$expect"
143
        printf "${red}#    actual: ${bold}%s${reset}\n" "$actual"
144
    fi
145

146
    echo    "not ok $count $testname" >>$LOG
147
    echo    "  expected: $expect"                     >>$LOG
148

149
    _bump $failures_file
150
}
151

152
###########
153
#  _bump  #  Increment a counter in a file
154
###########
155
function _bump() {
156
    local file=$1
157

158
    count=$(<$file)
159
    echo $(( $count + 1 )) >| $file
160
}
161

162
###############
163
#  test_port  #  Run curl against a port, check results against expectation
164
###############
165
function test_port() {
166
    local port="$1"              # e.g. 5000
167
    local op="$2"                # '=' or '~'
168
    local expect="$3"            # what to expect from curl output
169

170
    # -s -S means "silent, but show errors"
171
    local actual
172
    actual=$(curl --retry 3 --retry-all-errors -s -S http://127.0.0.1:$port/)
173
    local curl_rc=$?
174

175
    if [ $curl_rc -ne 0 ]; then
176
        _show_ok 0 "$testname - curl (port $port) failed with status $curl_rc"
177
        echo "# cat $WORKDIR/server.log:"
178
        cat $WORKDIR/server.log
179
        echo "# cat $logfile:"
180
        cat $logfile
181
        return
182
    fi
183

184
    case "$op" in
185
        '=')   is   "$actual" "$expect" "$testname : port $port" ;;
186
        '~')   like "$actual" "$expect" "$testname : port $port" ;;
187
        *)     die "Invalid operator '$op'" ;;
188
    esac
189
}
190

191

192
###################
193
#  start_service  #  Run the socket listener
194
###################
195
service_pid=
196
function start_service() {
197
    test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN"
198

199
    # FIXME: use ${testname} subdir but we can't: 50-char limit in runroot
200
    if ! is_rootless; then
201
        rm -rf $WORKDIR/{root,runroot,cni}
202
    else
203
        $PODMAN_BIN unshare rm -rf $WORKDIR/{root,runroot,cni}
204
    fi
205
    rm -f $DOCKER_SOCK
206
    mkdir --mode 0755 $WORKDIR/{root,runroot,cni}
207
    chcon --reference=/var/lib/containers $WORKDIR/root
208

209
    $PODMAN_BIN \
210
        --log-level debug \
211
        --storage-driver=vfs \
212
        --root $WORKDIR/root \
213
        --runroot $WORKDIR/runroot \
214
        --cgroup-manager=systemd \
215
        --network-config-dir $WORKDIR/cni \
216
        system service \
217
        --time 0 unix://$DOCKER_SOCK \
218
        &> $WORKDIR/server.log &
219
    service_pid=$!
220

221
    # Wait (FIXME: how do we test the socket?)
222
    local _timeout=5
223
    while [ $_timeout -gt 0 ]; do
224
        # FIXME: should we actually try a read or write?
225
        test -S $DOCKER_SOCK && return
226
        sleep 1
227
        _timeout=$(( $_timeout - 1 ))
228
    done
229
    cat $WORKDIR/server.log
230
    die "Timed out waiting for service"
231
}
232

233
############
234
#  podman  #  Needed by some test scripts to invoke the actual podman binary
235
############
236
function podman() {
237
    echo "\$ podman $*"           >>$WORKDIR/output.log
238
    output=$($PODMAN_BIN \
239
	--storage-driver=vfs \
240
        --root    $WORKDIR/root    \
241
        --runroot $WORKDIR/runroot \
242
        --network-config-dir $WORKDIR/cni \
243
        "$@")
244
    rc=$?
245

246
    echo -n "$output" >>$WORKDIR/output.log
247
    return $rc
248
}
249

250
# as rootless we want to test the remote connection so we add --connection
251
function podman_compose() {
252
    if is_rootless; then
253
        $PODMAN_BIN --connection compose-sock compose "$@"
254
    else
255
        podman compose "$@"
256
    fi
257
}
258

259
###################
260
#  random_string  #  Returns a pseudorandom human-readable string
261
###################
262
function random_string() {
263
    # Numeric argument, if present, is desired length of string
264
    local length=${1:-10}
265

266
    head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
267
}
268

269
# END   infrastructure code
270
###############################################################################
271
# BEGIN sanity checks
272

273
for tool in curl docker-compose; do
274
    type $tool &>/dev/null || die "$ME: Required tool '$tool' not found"
275
done
276

277
# END   sanity checks
278
###############################################################################
279
# BEGIN entry handler (subtest invoker)
280

281
# When rootless use a socket path accessible by the rootless user
282
if is_rootless; then
283
    # lets test two cases here, for rootless we try to connect to the connection as this should be respected
284
    DOCKER_SOCK="$WORKDIR/docker.sock"
285
    # use PODMAN_CONNECTIONS_CONF so we do not overwrite user settings
286
    PODMAN_CONNECTIONS_CONF="$WORKDIR/connections.json"
287
    export PODMAN_CONNECTIONS_CONF
288
    $PODMAN_BIN system connection add --default notexists "unix:///I/do/not/exist"
289
    $PODMAN_BIN system connection add compose-sock "unix://$DOCKER_SOCK"
290

291
else
292
    # export DOCKER_HOST docker-compose will use it
293
    DOCKER_HOST="unix://$DOCKER_SOCK"
294
    export DOCKER_HOST
295
fi
296

297
# hide annoying podman compose warnings, some tests want to check compose stderr and this breaks it.
298
CONTAINERS_CONF_OVERRIDE="$WORKDIR/containers.conf"
299
echo '[engine]
300
compose_warning_logs=false' > "$CONTAINERS_CONF_OVERRIDE"
301
export CONTAINERS_CONF_OVERRIDE
302

303

304
# Identify the tests to run. If called with args, use those as globs.
305
tests_to_run=()
306
if [ -n "$*" ]; then
307
    shopt -s nullglob
308
    for i; do
309
        match=(${TEST_ROOTDIR}/*${i}*/docker-compose.yml)
310
        if [ ${#match} -eq 0 ]; then
311
            die "No match for $TEST_ROOTDIR/*$i*.curl"
312
        fi
313
        tests_to_run+=("${match[@]}")
314
    done
315
    shopt -u nullglob
316
else
317
    tests_to_run=(${TEST_ROOTDIR}/*/docker-compose.yml)
318
fi
319

320
# Too hard to precompute the number of tests; just spit it out at the end.
321
n_tests=0
322

323
# We aren't really TAP 13; this helps logformatter recognize our output as BATS
324
echo "TAP version 13"
325

326
for t in "${tests_to_run[@]}"; do
327
    testdir="$(dirname $t)"
328
    testname="$(basename $testdir)"
329

330
    if [ -e $testdir/SKIP ]; then
331
        reason="$(<$testdir/SKIP)"
332
        if [ -n "$reason" ]; then
333
            reason=" - $reason"
334
        fi
335
        _show_ok skip "$testname # skip$reason"
336
        continue
337
    fi
338

339
    start_service
340

341
    logfile=$WORKDIR/$testname.log
342
    (
343
        cd $testdir || die "Cannot cd $testdir"
344

345
        if [ -e teardown.sh ]; then
346
            trap 'teardown' EXIT
347
            function teardown() {
348
                trap '_show_ok 0 "$testname - teardown" "[ok]" "[error]"' ERR
349
                . teardown.sh
350
                trap - ERR
351
            }
352
        fi
353

354
        # setup file may be used for creating temporary directories/files.
355
        # We source it so that envariables defined in it will get back to us.
356
        if [ -e setup.sh ]; then
357
            trap '_show_ok 0 "$testname - setup" "[ok]" "[error]"' ERR
358
            . setup.sh
359
            trap - ERR
360
        fi
361

362
        podman_compose up -d     &> $logfile
363
        docker_compose_rc=$?
364
        if [[ $docker_compose_rc -ne 0 ]]; then
365
            _show_ok 0 "$testname - up" "[ok]" "status=$docker_compose_rc"
366
            sed -e 's/^/#  /' <$logfile
367
            podman_compose down >>$logfile 2>&1      # No status check here
368
            exit 1
369
        fi
370
        _show_ok 1 "$testname - up"
371

372
        # Run tests. This is likely to be a series of 'test_port' checks
373
        # but may also include podman commands to inspect labels, state
374
        if [ -e tests.sh ]; then
375
            trap '_show_ok 0 "$testname - tests" "[ok]" "[error]"' ERR
376
            . tests.sh
377
            trap - ERR
378
        fi
379
        # FIXME: if any tests fail, try 'podman logs' on container?
380

381
        if [ -n "${COMPOSE_WAIT:-}" ]; then
382
            echo -n "Pausing due to \$COMPOSE_WAIT. Press ENTER to continue: "
383
            read keepgoing
384
        fi
385

386
        # Done. Clean up.
387
        podman_compose down     &>> $logfile
388
        rc=$?
389
        if [[ $rc -eq 0 ]]; then
390
            _show_ok 1 "$testname - down"
391
        else
392
            _show_ok 0 "$testname - down" "[ok]" "rc=$rc"
393
            # FIXME: show error
394
        fi
395
    )
396

397
    kill $service_pid
398
    wait $service_pid
399

400
    # FIXME: otherwise we get EBUSY
401
    if ! is_rootless; then
402
        umount $WORKDIR/root/overlay  &>/dev/null
403
    else
404
        $PODMAN_BIN unshare umount $WORKDIR/root/overlay  &>/dev/null
405
    fi
406

407
    # FIXME: run 'podman ps'?
408
#    rm -rf $WORKDIR/${testname}
409
done
410

411
# END   entry handler
412
###############################################################################
413

414
# Clean up
415

416
test_count=$(<$testcounter_file)
417
failure_count=$(<$failures_file)
418

419
if [ -z "${PODMAN_TESTS_KEEP_WORKDIR:-}" ]; then
420
    if ! is_rootless; then
421
        rm -rf $WORKDIR
422
    else
423
        $PODMAN_BIN unshare rm -rf $WORKDIR
424
    fi
425
fi
426

427
echo "1..${test_count}"
428

429
exit $failure_count
430

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.