summaryrefslogtreecommitdiff
path: root/test/system/160-volumes.bats
blob: 6829c6a78f3e3904b8c7cced32485649a2151db6 (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
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
#!/usr/bin/env bats   -*- bats -*-
#
# podman volume-related tests
#

load helpers

function setup() {
    basic_setup

    run_podman '?' volume rm -a
}

function teardown() {
    run_podman '?' rm -a --volumes
    run_podman '?' volume rm -t 0 -a -f

    basic_teardown
}


# Simple volume tests: share files between host and container
@test "podman run --volumes : basic" {
    run_podman volume list --noheading
    is "$output" "" "baseline: empty results from list --noheading"

    # Create three temporary directories
    vol1=${PODMAN_TMPDIR}/v1_$(random_string)
    vol2=${PODMAN_TMPDIR}/v2_$(random_string)
    vol3=${PODMAN_TMPDIR}/v3_$(random_string)
    mkdir $vol1 $vol2 $vol3

    # In each directory, write a random string to a file
    echo $(random_string) >$vol1/file1_in
    echo $(random_string) >$vol2/file2_in
    echo $(random_string) >$vol3/file3_in

    # Run 'cat' on each file, and compare against local files. Mix -v / --volume
    # flags, and specify them out of order just for grins. The shell wildcard
    # expansion must sort vol1/2/3 lexically regardless.
    v_opts="-v $vol1:/vol1:z --volume $vol3:/vol3:z -v $vol2:/vol2:z"
    run_podman run --rm $v_opts $IMAGE sh -c "cat /vol?/file?_in"

    for i in 1 2 3; do
        eval voldir=\$vol${i}
        is "${lines[$(($i - 1))]}" "$(< $voldir/file${i}_in)" \
           "contents of /vol${i}/file${i}_in"
    done

    # Confirm that container sees vol1 as a mount point
    run_podman run --rm $v_opts $IMAGE mount
    is "$output" ".* on /vol1 type .*" "'mount' in container lists vol1"

    # Have the container do write operations, confirm them on host
    out1=$(random_string)
    run_podman run --rm $v_opts $IMAGE sh -c "echo $out1 >/vol1/file1_out;
                                              cp /vol2/file2_in /vol3/file3_out"
    is "$(<$vol1/file1_out)" "$out1"              "contents of /vol1/file1_out"
    is "$(<$vol3/file3_out)" "$(<$vol2/file2_in)" "contents of /vol3/file3_out"

    # Writing to read-only volumes: not allowed
    run_podman 1 run --rm -v $vol1:/vol1ro:z,ro $IMAGE sh -c "touch /vol1ro/abc"
    is "$output" ".*Read-only file system"  "touch on read-only volume"
}


# Filter volumes by name
@test "podman volume filter --name" {
    suffix=$(random_string)
    prefix="volume"

    for i in 1 2; do
        myvolume=${prefix}_${i}_${suffix}
        run_podman volume create $myvolume
        is "$output" "$myvolume" "output from volume create $i"
    done

    run_podman volume ls --filter name=${prefix}_1.+ --format "{{.Name}}"
    is "$output" "${prefix}_1_${suffix}" "--filter name=${prefix}_1.+ shows only one volume"

    # The _1* is intentional as asterisk has different meaning in glob and regexp. Make sure this is regexp
    run_podman volume ls --filter name=${prefix}_1* --format "{{.Name}}"
    is "$output" "${prefix}_1_${suffix}.*${prefix}_2_${suffix}.*" "--filter name=${prefix}_1* shows ${prefix}_1_${suffix} and ${prefix}_2_${suffix}"

    for i in 1 2; do
        run_podman volume rm ${prefix}_${i}_${suffix}
    done
}

# Named volumes
@test "podman volume create / run" {
    myvolume=myvol$(random_string)
    mylabel=$(random_string)

    # Create a named volume
    run_podman volume create --label l=$mylabel  $myvolume
    is "$output" "$myvolume" "output from volume create"

    # Confirm that it shows up in 'volume ls', and confirm values
    run_podman volume ls --format json
    tests="
Name           | $myvolume
Driver         | local
Labels.l       | $mylabel
"
    parse_table "$tests" | while read field expect; do
        actual=$(jq -r ".[0].$field" <<<"$output")
        is "$actual" "$expect" "volume ls .$field"
    done

    # Run a container that writes to a file in that volume
    mountpoint=$(jq -r '.[0].Mountpoint' <<<"$output")
    rand=$(random_string)
    run_podman run --rm --volume $myvolume:/vol $IMAGE sh -c "echo $rand >/vol/myfile"

    # Confirm that the file is visible, with content, outside the container
    is "$(<$mountpoint/myfile)" "$rand" "we see content created in container"

    # Clean up
    run_podman volume rm $myvolume
}

# Removing volumes with --force
@test "podman volume rm --force" {
    run_podman run -d --volume myvol:/myvol $IMAGE top
    cid=$output
    run_podman 2 volume rm myvol
    is "$output" "Error: volume myvol is being used by the following container(s): $cid: volume is being used" "should error since container is running"
    run_podman volume rm myvol --force
}

# Running scripts (executables) from a volume
@test "podman volume: exec/noexec" {
    myvolume=myvol$(random_string)

    run_podman volume create $myvolume
    is "$output" "$myvolume" "output from volume create"

    run_podman volume inspect --format '{{.Mountpoint}}' $myvolume
    mountpoint="$output"

    # Create a script, make it runnable
    rand=$(random_string)
    cat >$mountpoint/myscript <<EOF
#!/bin/sh
echo "got here -$rand-"
EOF
    chmod 755 $mountpoint/myscript

    # By default, volumes are mounted exec, but we have manually added the
    # noexec option. This should fail.
    # ARGH. Unfortunately, runc (used for cgroups v1) has different exit status
    local expect_rc=126
    if [[ $(podman_runtime) = "runc" ]]; then
        expect_rc=1
    fi

    run_podman ${expect_rc} run --rm --volume $myvolume:/vol:noexec,z $IMAGE /vol/myscript
    # crun and runc emit different messages, and even runc is inconsistent
    # with itself (output changed some time in 2022?). Deal with all.
    assert "$output" =~ 'exec.* permission denied' "run on volume, noexec"

    # With the default, it should pass
    run_podman run --rm -v $myvolume:/vol:z $IMAGE /vol/myscript
    is "$output" "got here -$rand-" "script in volume is runnable with default (exec)"

    # Clean up
    run_podman volume rm $myvolume
}


# Anonymous temporary volumes, and persistent autocreated named ones
@test "podman volume, implicit creation with run" {
    # No hostdir arg: create anonymous container with random name
    rand=$(random_string)
    run_podman run -v /myvol $IMAGE sh -c "echo $rand >/myvol/myfile"

    run_podman volume ls -q
    tempvolume="$output"

    # We should see the file created in the container
    run_podman volume inspect --format '{{.Mountpoint}}' $tempvolume
    mountpoint="$output"
    test -e "$mountpoint/myfile"
    is "$(< $mountpoint/myfile)" "$rand" "file contents, anonymous volume"

    # Remove the container, using rm --volumes. Volume should now be gone.
    run_podman rm -a --volumes
    run_podman volume ls -q
    is "$output" "" "anonymous volume is removed after container is rm'ed"

    # Create a *named* container. This one should persist after container ends
    myvol=myvol$(random_string)
    rand=$(random_string)

    # Duplicate "-v" confirms #8307, fix for double-lock on same volume
    run_podman run --rm -v $myvol:/myvol:z -v $myvol:/myvol2:z $IMAGE \
               sh -c "echo $rand >/myvol/myfile"
    run_podman volume ls -q
    is "$output" "$myvol" "autocreated named container persists"

    # ...and should be usable, read/write, by a second container
    run_podman run --rm -v $myvol:/myvol:z $IMAGE \
               sh -c "cp /myvol/myfile /myvol/myfile2"

    run_podman volume rm $myvol

    if is_rootless; then
       # Autocreated volumes should also work with keep-id
       # All we do here is check status; podman 1.9.1 would fail with EPERM
       myvol=myvol$(random_string)
       run_podman run --rm -v $myvol:/myvol:z --userns=keep-id $IMAGE \
               touch /myvol/myfile
       run_podman volume rm $myvol
    fi
}


# Podman volume import test
@test "podman volume import test" {
    skip_if_remote "volumes import is not applicable on podman-remote"
    run_podman volume create my_vol
    run_podman run --rm -v my_vol:/data $IMAGE sh -c "echo hello >> /data/test"
    run_podman volume create my_vol2

    tarfile=${PODMAN_TMPDIR}/hello$(random_string | tr A-Z a-z).tar
    run_podman volume export my_vol --output=$tarfile
    # we want to use `run_podman volume export my_vol` but run_podman is wrapping EOF
    run_podman volume import my_vol2 - < $tarfile
    rm -f $tarfile
    run_podman run --rm -v my_vol2:/data $IMAGE sh -c "cat /data/test"
    is "$output" "hello" "output from second container"
    run_podman volume rm my_vol
    run_podman volume rm my_vol2
}

# Podman volume user test
@test "podman volume user test" {
    is_rootless || skip "only meaningful when run rootless"
    skip_if_remote "not applicable on podman-remote"

    user="1000:2000"
    newuser="100:200"
    tmpdir=${PODMAN_TMPDIR}/volume_$(random_string)
    mkdir $tmpdir
    touch $tmpdir/test1

    run_podman run --name user --user $user -v $tmpdir:/data:U $IMAGE stat -c "%u:%g" /data
    is "$output" "$user" "user should be changed"

    # Now chown the source directory and make sure recursive chown happens
    run_podman unshare chown -R $newuser $tmpdir
    run_podman start --attach user
    is "$output" "$user" "user should be the same"

    # Now chown the file in source directory and make sure recursive chown
    # doesn't happen
    run_podman unshare chown -R $newuser $tmpdir/test1
    run_podman start --attach user
    is "$output" "$user" "user should be the same"
    # test1 should still be chowned to $newuser
    run_podman unshare stat -c "%u:%g" $tmpdir/test1
    is "$output" "$newuser" "user should not be changed"

    run_podman unshare rm $tmpdir/test1
    run_podman rm user
}


# Confirm that container sees the correct id
@test "podman volume with --userns=keep-id" {
    is_rootless || skip "only meaningful when run rootless"

    myvoldir=${PODMAN_TMPDIR}/volume_$(random_string)
    mkdir $myvoldir
    touch $myvoldir/myfile

    # With keep-id
    run_podman run --rm -v $myvoldir:/vol:z --userns=keep-id $IMAGE \
               stat -c "%u:%s" /vol/myfile
    is "$output" "$(id -u):0" "with keep-id: stat(file in container) == my uid"

    # Without
    run_podman run --rm -v $myvoldir:/vol:z $IMAGE \
               stat -c "%u:%s" /vol/myfile
    is "$output" "0:0" "w/o keep-id: stat(file in container) == root"
}


# 'volume prune' identifies and cleans up unused volumes
@test "podman volume prune" {
    # Create four named volumes
    local -a v=()
    for i in 1 2 3 4;do
        vol=myvol${i}$(random_string)
        v[$i]=$vol
        run_podman volume create $vol
    done

    # Create two additional labeled volumes
    for i in 5 6; do
        vol=myvol${i}$(random_string)
        v[$i]=$vol
        run_podman volume create $vol --label "mylabel"
    done

    # (Assert that output is formatted, not a one-line blob: #8011)
    run_podman volume inspect ${v[1]}
    assert "${#lines[*]}" -ge 10 "Output from 'volume inspect'; see #8011"

    # Run two containers: one mounting v1, one mounting v2 & v3
    run_podman run --name c1 --volume ${v[1]}:/vol1 $IMAGE date
    run_podman run --name c2 --volume ${v[2]}:/vol2 -v ${v[3]}:/vol3 \
               $IMAGE date

    # List available volumes for pruning after using 1,2,3
    run_podman volume prune <<< N
    is "$(echo $(sort <<<${lines[@]:1:3}))" "${v[4]} ${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use, lists 4,5,6"

    # List available volumes for pruning after using 1,2,3 and filtering; see #8913
    run_podman volume prune --filter label=mylabel <<< N
    is "$(echo $(sort <<<${lines[@]:1:2}))" "${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use and 4 filtered out, lists 5,6"

    # prune should remove v4
    run_podman volume prune --force
    is "$(echo $(sort <<<$output))" "${v[4]} ${v[5]} ${v[6]}" \
       "volume prune, with 1, 2, 3 in use, deletes only 4, 5, 6"

    # Remove the container using v2 and v3. Prune should now remove those.
    # The 'echo sort' is to get the output sorted and in one line.
    run_podman rm c2
    run_podman volume prune --force
    is "$(echo $(sort <<<$output))" "${v[2]} ${v[3]}" \
       "volume prune, after rm c2, deletes volumes 2 and 3"

    # Remove the final container. Prune should now remove v1.
    run_podman rm c1
    run_podman volume prune --force
    is "$output"  "${v[1]}" "volume prune, after rm c2 & c1, deletes volume 1"

    # Further prunes are NOPs
    run_podman volume prune --force
    is "$output"  "" "no more volumes to prune"
}

@test "podman volume type=bind" {
    myvoldir=${PODMAN_TMPDIR}/volume_$(random_string)
    mkdir $myvoldir
    touch $myvoldir/myfile

    myvolume=myvol$(random_string)
    run_podman 125 volume create -o type=bind -o device=/bogus $myvolume
    is "$output" "Error: invalid volume option device for driver 'local': stat /bogus: no such file or directory" "should fail with bogus directory not existing"

    run_podman volume create -o type=bind -o device=/$myvoldir $myvolume
    is "$output" "$myvolume" "should successfully create myvolume"

    run_podman run --rm -v $myvolume:/vol:z $IMAGE \
               stat -c "%u:%s" /vol/myfile
    is "$output" "0:0" "w/o keep-id: stat(file in container) == root"
}

@test "podman volume type=tmpfs" {
    myvolume=myvol$(random_string)
    run_podman volume create -o type=tmpfs -o device=tmpfs $myvolume
    is "$output" "$myvolume" "should successfully create myvolume"

    run_podman run --rm -v $myvolume:/vol $IMAGE stat -f -c "%T" /vol
    is "$output" "tmpfs" "volume should be tmpfs"
}

# Named volumes copyup
@test "podman volume create copyup" {
    myvolume=myvol$(random_string)
    mylabel=$(random_string)

    # Create a named volume
    run_podman volume create $myvolume
    is "$output" "$myvolume" "output from volume create"

    # Confirm that it shows up in 'volume ls', and confirm values
    run_podman volume ls --format json
    tests="
Name           | $myvolume
Driver         | local
NeedsCopyUp    | true
NeedsChown    | true
"
    parse_table "$tests" | while read field expect; do
        actual=$(jq -r ".[0].$field" <<<"$output")
        is "$actual" "$expect" "volume ls .$field"
    done

    run_podman run --rm --volume $myvolume:/vol $IMAGE true
    run_podman volume inspect --format '{{ .NeedsCopyUp }}' $myvolume
    is "${output}" "true" "If content in dest '/vol' empty NeedsCopyUP should still be true"
    run_podman volume inspect --format '{{ .NeedsChown }}' $myvolume
    is "${output}" "false" "After first use within a container NeedsChown should still be false"

    run_podman run --rm --volume $myvolume:/etc $IMAGE ls /etc/passwd
    run_podman volume inspect --format '{{ .NeedsCopyUp }}' $myvolume
    is "${output}" "false" "If content in dest '/etc' non-empty NeedsCopyUP should still have happened and be false"

    run_podman volume inspect --format '{{.Mountpoint}}' $myvolume
    mountpoint="$output"
    test -e "$mountpoint/passwd"

    # Clean up
    run_podman volume rm $myvolume
}

@test "podman volume mount" {
    skip_if_remote "podman --remote volume mount not supported"
    myvolume=myvol$(random_string)
    myfile=myfile$(random_string)
    mytext=$(random_string)

    # Create a named volume
    run_podman volume create $myvolume
    is "$output" "$myvolume" "output from volume create"

    if ! is_rootless ; then
        # image mount is hard to test as a rootless user
        # and does not work remotely
        run_podman volume mount ${myvolume}
        mnt=${output}
	echo $mytext >$mnt/$myfile
        run_podman run -v ${myvolume}:/vol:z $IMAGE cat /vol/$myfile
	is "$output" "$mytext" "$myfile should exist within the containers volume and contain $mytext"
        run_podman volume unmount ${myvolume}
    else
        run_podman 125 volume mount ${myvolume}
	is "$output" "Error: cannot run command \"podman volume mount\" in rootless mode, must execute.*podman unshare.*first" "Should fail and complain about unshare"
    fi
}

@test "podman --image-volume" {
    tmpdir=$PODMAN_TMPDIR/volume-test
    mkdir -p $tmpdir
    containerfile=$tmpdir/Containerfile
    cat >$containerfile <<EOF
FROM $IMAGE
VOLUME /data
EOF
    fs=$(stat -f -c %T .)
    run_podman build -t volume_image $tmpdir

    containersconf=$tmpdir/containers.conf
    cat >$containersconf <<EOF
[engine]
image_volume_mode="tmpfs"
EOF

    run_podman run --image-volume tmpfs --rm volume_image stat -f -c %T /data
    is "$output" "tmpfs" "Should be tmpfs"

    run_podman 1 run --image-volume ignore --rm volume_image stat -f -c %T /data
    is "$output" "stat: can't read file system information for '/data': No such file or directory" "Should fail with /data does not exists"

    CONTAINERS_CONF="$containersconf" run_podman run --rm volume_image stat -f -c %T /data
    is "$output" "tmpfs" "Should be tmpfs"

    CONTAINERS_CONF="$containersconf" run_podman run --image-volume bind --rm volume_image stat -f -c %T /data
    assert "$output" != "tmpfs" "Should match hosts $fs"

    CONTAINERS_CONF="$containersconf" run_podman run --image-volume tmpfs --rm volume_image stat -f -c %T /data
    is "$output" "tmpfs" "Should be tmpfs"

    CONTAINERS_CONF="$containersconf" run_podman 1 run --image-volume ignore --rm volume_image stat -f -c %T /data
    is "$output" "stat: can't read file system information for '/data': No such file or directory" "Should fail with /data does not exists"

    run_podman rm --all --force -t 0
    run_podman image rm --force localhost/volume_image
}

@test "podman volume rm --force bogus" {
    run_podman 1 volume rm bogus
    is "$output" "Error: no volume with name \"bogus\" found: no such volume" "Should print error"
    run_podman volume rm --force bogus
    is "$output" "" "Should print no output"
}

# vim: filetype=sh