File: basic.sh

package info (click to toggle)
incus 6.0.5-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 26,092 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (722 lines) | stat: -rw-r--r-- 29,180 bytes parent folder | download | duplicates (2)
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
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
test_basic_usage() {
    # shellcheck disable=2039,3043
    local incus_backend
    incus_backend=$(storage_backend "$INCUS_DIR")

    ensure_import_testimage

    # shellcheck disable=2153
    ensure_has_localhost_remote "${INCUS_ADDR}"

    # Test image export
    sum="$(incus image info testimage | awk '/^Fingerprint/ {print $2}')"
    incus image export testimage "${INCUS_DIR}/"
    [ "${sum}" = "$(sha256sum "${INCUS_DIR}/${sum}.tar.xz" | cut -d' ' -f1)" ]

    # Test an alias with slashes
    incus image show "${sum}"
    incus image alias create a/b "${sum}"
    incus image alias delete a/b

    # Test alias list filtering
    incus image alias create foo "${sum}"
    incus image alias create bar "${sum}"
    incus image alias list local: | grep -q foo
    incus image alias list local: | grep -q bar
    incus image alias list local: foo | grep -q -v bar
    incus image alias list local: "${sum}" | grep -q foo
    incus image alias list local: non-existent | grep -q -v non-existent
    incus image alias delete foo
    incus image alias delete bar

    incus image alias create foo "${sum}"
    incus image alias rename foo bar
    incus image alias list | grep -qv foo # the old name is gone
    incus image alias delete bar

    # Test an alias with description
    incus image alias create baz "${sum}" --description "Test description"
    incus image alias list | grep -q 'Test description'
    incus image alias delete baz

    # Test image list output formats (table & json)
    incus image list --format table | grep -q testimage
    incus image list --format json |
        jq '.[]|select(.alias[0].name="testimage")' |
        grep -q '"name": "testimage"'

    # Test image delete
    incus image delete testimage

    # test GET /1.0, since the client always puts to /1.0/
    my_curl -f -X GET "https://${INCUS_ADDR}/1.0"
    my_curl -f -X GET "https://${INCUS_ADDR}/1.0/instances"

    # Re-import the image
    mv "${INCUS_DIR}/${sum}.tar.xz" "${INCUS_DIR}/testimage.tar.xz"
    incus image import "${INCUS_DIR}/testimage.tar.xz" --alias testimage user.foo=bar --public
    incus image show testimage | grep -qF "user.foo: bar"
    incus image show testimage | grep -qF "public: true"
    incus image delete testimage
    incus image import "${INCUS_DIR}/testimage.tar.xz" --alias testimage
    rm "${INCUS_DIR}/testimage.tar.xz"

    # Test filename for image export
    incus image export testimage "${INCUS_DIR}/"
    [ "${sum}" = "$(sha256sum "${INCUS_DIR}/${sum}.tar.xz" | cut -d' ' -f1)" ]
    rm "${INCUS_DIR}/${sum}.tar.xz"

    # Test custom filename for image export
    incus image export testimage "${INCUS_DIR}/foo"
    [ "${sum}" = "$(sha256sum "${INCUS_DIR}/foo.tar.xz" | cut -d' ' -f1)" ]
    rm "${INCUS_DIR}/foo.tar.xz"

    # Test image export with a split image.
    deps/import-busybox --split --alias splitimage

    sum="$(incus image info splitimage | awk '/^Fingerprint/ {print $2}')"

    incus image export splitimage "${INCUS_DIR}"
    [ "${sum}" = "$(cat "${INCUS_DIR}/meta-${sum}.tar.xz" "${INCUS_DIR}/${sum}.tar.xz" | sha256sum | cut -d' ' -f1)" ]

    # Delete the split image and exported files
    rm "${INCUS_DIR}/${sum}.tar.xz"
    rm "${INCUS_DIR}/meta-${sum}.tar.xz"
    incus image delete splitimage

    # Redo the split image export test, this time with the --filename flag
    # to tell import-busybox to set the 'busybox' filename in the upload.
    # The sum should remain the same as its the same image.
    deps/import-busybox --split --filename --alias splitimage

    incus image export splitimage "${INCUS_DIR}"
    [ "${sum}" = "$(cat "${INCUS_DIR}/meta-${sum}.tar.xz" "${INCUS_DIR}/${sum}.tar.xz" | sha256sum | cut -d' ' -f1)" ]

    # Delete the split image and exported files
    rm "${INCUS_DIR}/${sum}.tar.xz"
    rm "${INCUS_DIR}/meta-${sum}.tar.xz"
    incus image delete splitimage

    # Test --no-profiles flag
    poolName=$(incus profile device get default root pool)
    ! incus init testimage foo --no-profiles || false
    incus init testimage foo --no-profiles -s "${poolName}"
    incus delete -f foo

    # Test container creation
    incus init testimage foo
    incus list | grep foo | grep STOPPED
    incus list fo | grep foo | grep STOPPED # codespell:ignore fo

    # Test list json format
    incus list --format json | jq '.[]|select(.name="foo")' | grep '"name": "foo"'

    # Test list with --columns and --fast
    ! incus list --columns=nsp --fast || false

    # Check volatile.apply_template is correct.
    incus config get foo volatile.apply_template | grep create

    # Start the instance to clear apply_template.
    incus start foo
    incus stop foo -f

    # Test container rename
    incus move foo bar

    # Check volatile.apply_template is altered during rename.
    incus config get bar volatile.apply_template | grep rename

    incus list | grep -v foo
    incus list | grep bar

    incus rename bar foo
    incus list | grep -v bar
    incus list | grep foo
    incus rename foo bar

    # Test container copy
    incus copy bar foo
    incus delete foo

    # gen untrusted cert
    gen_cert client3

    # don't allow requests without a cert to get trusted data
    curl -k -s -X GET "https://${INCUS_ADDR}/1.0/instances/foo" | grep 403

    # Test unprivileged container publish
    incus publish bar --alias=foo-image prop1=val1
    incus image show foo-image | grep val1
    curl -k -s --cert "${INCUS_CONF}/client3.crt" --key "${INCUS_CONF}/client3.key" -X GET "https://${INCUS_ADDR}/1.0/images" | grep -F "/1.0/images/" && false
    incus image delete foo-image

    # Test container publish with existing alias
    incus publish bar --alias=foo-image --alias=foo-image2
    incus launch testimage baz
    # change the container filesystem so the resulting image is different
    incus exec baz -- touch /somefile
    incus stop baz --force
    # publishing another image with same alias should fail
    ! incus publish baz --alias=foo-image || false
    # publishing another image with same alias and '--reuse' flag should success
    incus publish baz --alias=foo-image --reuse
    fooImage=$(incus image list -cF -fcsv foo-image)
    fooImage2=$(incus image list -cF -fcsv foo-image2)
    incus delete baz
    incus image delete foo-image foo-image2

    # the first image should have foo-image2 alias and the second imgae foo-image alias
    if [ "$fooImage" = "$fooImage2" ]; then
        echo "foo-image and foo-image2 aliases should be assigned to two different images"
        false
    fi

    # Test container publish with existing alias
    incus publish bar --alias=foo-image --alias=foo-image2
    incus launch testimage baz
    # change the container filesystem so the resulting image is different
    incus exec baz -- touch /somefile
    incus stop baz --force
    # publishing another image with same aliases
    incus publish baz --alias=foo-image --alias=foo-image2 --reuse
    fooImage=$(incus image list -cF -fcsv foo-image)
    fooImage2=$(incus image list -cF -fcsv foo-image2)
    incus delete baz
    incus image delete foo-image

    # the second image should have foo-image and foo-image2 aliases and the first one should be removed
    if [ "$fooImage" != "$fooImage2" ]; then
        echo "foo-image and foo-image2 aliases should be assigned to the same image"
        false
    fi

    # Test image compression on publish
    incus publish bar --alias=foo-image-compressed --compression=bzip2 prop=val1
    incus image show foo-image-compressed | grep val1
    curl -k -s --cert "${INCUS_CONF}/client3.crt" --key "${INCUS_CONF}/client3.key" -X GET "https://${INCUS_ADDR}/1.0/images" | grep -F "/1.0/images/" && false
    incus image delete foo-image-compressed

    # Test compression options
    incus publish bar --alias=foo-image-compressed --compression="gzip --rsyncable" prop=val1
    incus image delete foo-image-compressed

    # Test privileged container publish
    incus profile create priv
    incus profile set priv security.privileged true
    incus init testimage barpriv -p default -p priv
    incus publish barpriv --alias=foo-image prop1=val1
    incus image show foo-image | grep val1
    curl -k -s --cert "${INCUS_CONF}/client3.crt" --key "${INCUS_CONF}/client3.key" -X GET "https://${INCUS_ADDR}/1.0/images" | grep -F "/1.0/images/" && false
    incus image delete foo-image
    incus delete barpriv
    incus profile delete priv

    # Test that containers without metadata.yaml are published successfully.
    # Note that this quick hack won't work for LVM, since it doesn't always mount
    # the container's filesystem. That's ok though: the logic we're trying to
    # test here is independent of storage backend, so running it for just one
    # backend (or all non-lvm backends) is enough.
    if [ "$incus_backend" = "lvm" ]; then
        incus init testimage nometadata
        rm -f "${INCUS_DIR}/containers/nometadata/metadata.yaml"
        incus publish nometadata --alias=nometadata-image
        incus image delete nometadata-image
        incus delete nometadata
    fi

    # Test public images
    incus publish --public bar --alias=foo-image2
    curl -k -s --cert "${INCUS_CONF}/client3.crt" --key "${INCUS_CONF}/client3.key" -X GET "https://${INCUS_ADDR}/1.0/images" | grep -F "/1.0/images/"
    incus image delete foo-image2

    # Test invalid instance names
    ! incus init testimage -abc || false
    ! incus init testimage abc- || false
    ! incus init testimage 1234 || false
    ! incus init testimage foo.bar || false
    ! incus init testimage a_b_c || false
    ! incus init testimage aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || false

    # Test snapshot publish
    incus snapshot create bar
    incus publish bar/snap0 --alias foo
    incus init foo bar2
    incus list | grep bar2
    incus delete bar2
    incus image delete foo

    # Test alias support
    cp "${INCUS_CONF}/config.yml" "${INCUS_CONF}/config.yml.bak"

    #   1. Basic built-in alias functionality
    [ "$(incus ls)" = "$(incus list)" ]
    #   2. Basic user-defined alias functionality
    printf "aliases:\\n  l: list\\n" >> "${INCUS_CONF}/config.yml"
    [ "$(incus l)" = "$(incus list)" ]
    #   3. Built-in aliases and user-defined aliases can coexist
    [ "$(incus ls)" = "$(incus l)" ]
    #   4. Multi-argument alias keys and values
    echo "  i ls: image list" >> "${INCUS_CONF}/config.yml"
    [ "$(incus i ls)" = "$(incus image list)" ]
    #   5. Aliases where len(keys) != len(values) (expansion/contraction of number of arguments)
    printf "  ils: image list\\n  container ls: list\\n" >> "${INCUS_CONF}/config.yml"
    [ "$(incus ils)" = "$(incus image list)" ]
    [ "$(incus container ls)" = "$(incus list)" ]
    #   6. User-defined aliases override built-in aliases
    echo "  cp: list" >> "${INCUS_CONF}/config.yml"
    [ "$(incus ls)" = "$(incus cp)" ]
    #   7. User-defined aliases override commands and don't recurse
    incus init testimage foo
    INC_CONFIG_SHOW=$(incus config show foo --expanded)
    echo "  config show: config show --expanded" >> "${INCUS_CONF}/config.yml"
    [ "$(incus config show foo)" = "$INC_CONFIG_SHOW" ]
    incus delete foo

    # Restore the config to remove the aliases
    mv "${INCUS_CONF}/config.yml.bak" "${INCUS_CONF}/config.yml"

    # Delete the bar container we've used for several tests
    incus delete bar

    # incus delete should also delete all snapshots of bar
    [ ! -d "${INCUS_DIR}/containers-snapshots/bar" ]

    # Test randomly named container creation
    incus launch testimage
    RDNAME=$(incus list --format csv --columns n)
    incus delete -f "${RDNAME}"

    # Test "nonetype" container creation
    wait_for "${INCUS_ADDR}" my_curl -X POST "https://${INCUS_ADDR}/1.0/instances" \
        -d "{\"name\":\"nonetype\",\"source\":{\"type\":\"none\"}}"
    incus delete nonetype

    # Test "nonetype" container creation with an LXC config
    wait_for "${INCUS_ADDR}" my_curl -X POST "https://${INCUS_ADDR}/1.0/instances" \
        -d "{\"name\":\"configtest\",\"config\":{\"raw.lxc\":\"lxc.hook.clone=/bin/true\"},\"source\":{\"type\":\"none\"}}"
    # shellcheck disable=SC2102
    [ "$(my_curl "https://${INCUS_ADDR}/1.0/instances/configtest" | jq -r .metadata.config[\"raw.lxc\"])" = "lxc.hook.clone=/bin/true" ]
    incus delete configtest

    # Test activateifneeded/shutdown
    INCUS_ACTIVATION_DIR=$(mktemp -d -p "${TEST_DIR}" XXX)
    chmod +x "${INCUS_ACTIVATION_DIR}"
    spawn_incus "${INCUS_ACTIVATION_DIR}" true
    (
        set -e
        # shellcheck disable=SC2030
        INCUS_DIR=${INCUS_ACTIVATION_DIR}
        ensure_import_testimage
        incusd activateifneeded --debug 2>&1 | grep -qF "Daemon has core.https_address set, activating..."
        incus config unset core.https_address --force-local
        incusd activateifneeded --debug 2>&1 | grep -qF -v "activating..."
        incus init testimage autostart --force-local
        incusd activateifneeded --debug 2>&1 | grep -qF -v "activating..."
        incus config set autostart boot.autostart true --force-local

        # Restart the daemon, this forces the global database to be dumped to disk.
        shutdown_incus "${INCUS_DIR}"
        respawn_incus "${INCUS_DIR}" true
        incus stop --force autostart --force-local

        incusd activateifneeded --debug 2>&1 | grep -qF "Daemon has auto-started instances, activating..."

        incus config unset autostart boot.autostart --force-local
        incusd activateifneeded --debug 2>&1 | grep -qF -v "activating..."

        incus start autostart --force-local
        PID=$(incus info autostart --force-local | awk '/^PID:/ {print $2}')
        shutdown_incus "${INCUS_DIR}"
        [ -d "/proc/${PID}" ] && false

        incusd activateifneeded --debug 2>&1 | grep -qF "Daemon has auto-started instances, activating..."

        # shellcheck disable=SC2031
        respawn_incus "${INCUS_DIR}" true

        incus list --force-local autostart | grep -q RUNNING

        # Check for scheduled instance snapshots
        incus stop --force autostart --force-local
        incus config set autostart snapshots.schedule "* * * * *" --force-local
        shutdown_incus "${INCUS_DIR}"
        incusd activateifneeded --debug 2>&1 | grep -qF "Daemon has scheduled instance snapshots, activating..."

        # shellcheck disable=SC2031
        respawn_incus "${INCUS_DIR}" true

        incus config unset autostart snapshots.schedule --force-local

        # Check for scheduled volume snapshots
        storage_pool="incustest-$(basename "${INCUS_DIR}")"

        incus storage volume create "${storage_pool}" vol --force-local

        shutdown_incus "${INCUS_DIR}"
        incusd activateifneeded --debug 2>&1 | grep -qF -v "activating..."

        # shellcheck disable=SC2031
        respawn_incus "${INCUS_DIR}" true

        incus storage volume set "${storage_pool}" vol snapshots.schedule="* * * * *" --force-local

        shutdown_incus "${INCUS_DIR}"
        incusd activateifneeded --debug 2>&1 | grep -qF "Daemon has scheduled volume snapshots, activating..."

        # shellcheck disable=SC2031
        respawn_incus "${INCUS_DIR}" true

        incus delete autostart --force --force-local
        incus storage volume delete "${storage_pool}" vol --force-local
    )
    # shellcheck disable=SC2031,2269
    INCUS_DIR=${INCUS_DIR}
    kill_incus "${INCUS_ACTIVATION_DIR}"

    # Create and start a container
    incus launch testimage foo --description "Test container"
    incus list | grep foo | grep RUNNING
    incus stop foo --force

    # cycle it a few times
    incus start foo
    mac1=$(incus exec foo -- cat /sys/class/net/eth0/address)
    incus stop foo --force
    incus start foo
    mac2=$(incus exec foo -- cat /sys/class/net/eth0/address)

    if [ -n "${mac1}" ] && [ -n "${mac2}" ] && [ "${mac1}" != "${mac2}" ]; then
        echo "==> MAC addresses didn't match across restarts (${mac1} vs ${mac2})"
        false
    fi

    # Test instance types
    incus launch testimage test-limits -t c0.5-m0.2
    [ "$(incus config get test-limits limits.cpu)" = "1" ]
    [ "$(incus config get test-limits limits.cpu.allowance)" = "50%" ]
    [ "$(incus config get test-limits limits.memory)" = "204MiB" ]

    # Test CPU allocation information
    [ "$(incus query /1.0/instances/test-limits/state | jq -r '.cpu.allocated_time')" = "1000000000" ]
    incus config set test-limits limits.cpu.allowance 100ms/200ms
    [ "$(incus query /1.0/instances/test-limits/state | jq -r '.cpu.allocated_time')" = "500000000" ]
    incus config set test-limits limits.cpu 2
    [ "$(incus query /1.0/instances/test-limits/state | jq -r '.cpu.allocated_time')" = "500000000" ]
    incus config unset test-limits limits.cpu.allowance
    [ "$(incus query /1.0/instances/test-limits/state | jq -r '.cpu.allocated_time')" = "2000000000" ]
    incus config unset test-limits limits.cpu
    [ "$(incus query /1.0/instances/test-limits/state | jq -r '.cpu.allocated_time')" = "$(nproc)000000000" ]

    incus delete -f test-limits

    # Test last_used_at field is working properly
    incus init testimage last-used-at-test
    incus list last-used-at-test --format json | jq -r '.[].last_used_at' | grep '1970-01-01T00:00:00Z'
    incus start last-used-at-test
    incus list last-used-at-test --format json | jq -r '.[].last_used_at' | grep -v '1970-01-01T00:00:00Z'
    incus delete last-used-at-test --force

    # Test user, group and cwd
    incus exec foo -- mkdir /blah
    [ "$(incus exec foo --user 1000 -- id -u)" = "1000" ] || false
    [ "$(incus exec foo --group 1000 -- id -g)" = "1000" ] || false
    [ "$(incus exec foo --cwd /blah -- pwd)" = "/blah" ] || false

    [ "$(incus exec foo --user 1234 --group 5678 --cwd /blah -- id -u)" = "1234" ] || false
    [ "$(incus exec foo --user 1234 --group 5678 --cwd /blah -- id -g)" = "5678" ] || false
    [ "$(incus exec foo --user 1234 --group 5678 --cwd /blah -- pwd)" = "/blah" ] || false

    # check that we can set the environment
    incus exec foo -- pwd | grep /root
    incus exec --env BEST_BAND=meshuggah foo -- env | grep meshuggah
    incus exec foo -- ip link show | grep eth0

    # check that we can get the return code for a non- wait-for-websocket exec
    op=$(my_curl -X POST "https://${INCUS_ADDR}/1.0/instances/foo/exec" -d '{"command": ["echo", "test"], "environment": {}, "wait-for-websocket": false, "interactive": false}' | jq -r .operation)
    [ "$(my_curl "https://${INCUS_ADDR}${op}/wait" | jq -r .metadata.metadata.return)" != "null" ]

    # test file transfer
    echo abc > "${INCUS_DIR}/in"

    incus file push "${INCUS_DIR}/in" foo/root/
    incus exec foo -- /bin/cat /root/in | grep -xF abc
    incus exec foo -- /bin/rm -f root/in

    incus file push "${INCUS_DIR}/in" foo/root/in1
    incus exec foo -- /bin/cat /root/in1 | grep -xF abc
    incus exec foo -- /bin/rm -f root/in1

    # test incus file edit doesn't change target file's owner and permissions
    echo "content" | incus file push - foo/tmp/edit_test
    incus exec foo -- chown 55:55 /tmp/edit_test
    incus exec foo -- chmod 555 /tmp/edit_test
    echo "new content" | incus file edit foo/tmp/edit_test
    [ "$(incus exec foo -- cat /tmp/edit_test)" = "new content" ]
    [ "$(incus exec foo -- stat -c \"%u %g %a\" /tmp/edit_test)" = "55 55 555" ]

    # make sure stdin is chowned to our container root uid (Issue #590)
    [ -t 0 ] && [ -t 1 ] && incus exec foo -- chown 1000:1000 /proc/self/fd/0

    echo foo | incus exec foo -- tee /tmp/foo

    # test exec with/without "--" separator
    incus exec foo -- true
    incus exec foo true

    # Detect regressions/hangs in exec
    sum=$(ps aux | tee "${INCUS_DIR}/out" | incus exec foo -- md5sum | cut -d' ' -f1)
    [ "${sum}" = "$(md5sum "${INCUS_DIR}/out" | cut -d' ' -f1)" ]
    rm "${INCUS_DIR}/out"

    # FIXME: make this backend agnostic
    if [ "$incus_backend" = "dir" ]; then
        content=$(cat "${INCUS_DIR}/containers/foo/rootfs/tmp/foo")
        [ "${content}" = "foo" ]
    fi

    incus launch testimage deleterunning
    my_curl -X DELETE "https://${INCUS_ADDR}/1.0/instances/deleterunning" | grep "Instance is running"
    incus delete deleterunning -f

    # cleanup
    incus delete foo -f

    if [ -e /sys/module/apparmor/ ]; then
        # check that an apparmor profile is created for this container, that it is
        # unloaded on stop, and that it is deleted when the container is deleted
        incus launch testimage inc-apparmor-test

        MAJOR=0
        MINOR=0
        if [ -f /sys/kernel/security/apparmor/features/domain/version ]; then
            MAJOR=$(awk -F. '{print $1}' < /sys/kernel/security/apparmor/features/domain/version)
            MINOR=$(awk -F. '{print $2}' < /sys/kernel/security/apparmor/features/domain/version)
        fi

        if [ "${MAJOR}" -gt "1" ] || { [ "${MAJOR}" = "1" ] && [ "${MINOR}" -ge "2" ]; }; then
            aa_namespace="incus-inc-apparmor-test_<$(echo "${INCUS_DIR}" | sed -e 's/\//-/g' -e 's/^.//')>"
            aa-status | grep -q ":${aa_namespace}:unconfined" || aa-status | grep -qF ":${aa_namespace}://unconfined"
            incus stop inc-apparmor-test --force
            ! aa-status | grep -qF ":${aa_namespace}:" || false
        else
            aa-status | grep "incus-inc-apparmor-test_<${INCUS_DIR}>"
            incus stop inc-apparmor-test --force
            ! aa-status | grep -qF "incus-inc-apparmor-test_<${INCUS_DIR}>" || false
        fi
        incus delete inc-apparmor-test
        [ ! -f "${INCUS_DIR}/security/apparmor/profiles/incus-inc-apparmor-test" ]
    else
        echo "==> SKIP: apparmor tests (missing kernel support)"
    fi

    if [ "$(awk '/^Seccomp:/ {print $2}' "/proc/self/status")" -eq "0" ]; then
        incus launch testimage inc-seccomp-test
        init=$(incus info inc-seccomp-test | awk '/^PID:/ {print $2}')
        [ "$(awk '/^Seccomp:/ {print $2}' "/proc/${init}/status")" -eq "2" ]
        incus stop --force inc-seccomp-test
        incus config set inc-seccomp-test security.syscalls.deny_default false
        incus start inc-seccomp-test
        init=$(incus info inc-seccomp-test | awk '/^PID:/ {print $2}')
        [ "$(awk '/^Seccomp:/ {print $2}' "/proc/${init}/status")" -eq "0" ]
        incus delete --force inc-seccomp-test
    else
        echo "==> SKIP: seccomp tests (seccomp filtering is externally enabled)"
    fi

    # make sure that privileged containers are not world-readable
    incus profile create unconfined
    incus profile set unconfined security.privileged true
    incus init testimage foo2 -p unconfined -s "incustest-$(basename "${INCUS_DIR}")"
    [ "$(stat -L -c "%a" "${INCUS_DIR}/containers/foo2")" = "100" ]
    incus delete foo2
    incus profile delete unconfined

    # Test boot.host_shutdown_timeout config setting
    incus init testimage configtest --config boot.host_shutdown_timeout=45
    [ "$(incus config get configtest boot.host_shutdown_timeout)" -eq 45 ]
    incus config set configtest boot.host_shutdown_timeout 15
    [ "$(incus config get configtest boot.host_shutdown_timeout)" -eq 15 ]
    incus delete configtest

    # Test deleting multiple images
    # Start 3 containers to create 3 different images
    incus launch testimage c1
    incus launch testimage c2
    incus launch testimage c3
    incus exec c1 -- touch /tmp/c1
    incus exec c2 -- touch /tmp/c2
    incus exec c3 -- touch /tmp/c3
    incus publish --force c1 --alias=image1
    incus publish --force c2 --alias=image2
    incus publish --force c3 --alias=image3
    # Delete multiple images with incus delete and confirm they're deleted
    incus image delete local:image1 local:image2 local:image3
    ! incus image list | grep -q image1 || false
    ! incus image list | grep -q image2 || false
    ! incus image list | grep -q image3 || false
    # Cleanup the containers
    incus delete --force c1 c2 c3

    # Test --all flag
    incus init testimage c1
    incus init testimage c2
    incus start --all
    incus list | grep c1 | grep RUNNING
    incus list | grep c2 | grep RUNNING
    ! incus stop --all c1 || false
    incus stop --all -f
    incus list | grep c1 | grep STOPPED
    incus list | grep c2 | grep STOPPED
    # Cleanup the containers
    incus delete --force c1 c2

    # Ephemeral
    incus launch testimage foo -e
    OLD_INIT=$(incus info foo | awk '/^PID:/ {print $2}')

    REBOOTED="false"

    for _ in $(seq 60); do
        NEW_INIT=$(incus info foo | awk '/^PID:/ {print $2}' || true)

        # If init process is running, check if is old or new process.
        if [ -n "${NEW_INIT}" ]; then
            if [ "${OLD_INIT}" != "${NEW_INIT}" ]; then
                REBOOTED="true"
                break
            else
                incus exec foo -- reboot || true # Signal to running old init process to reboot if not rebooted yet.
            fi
        fi

        sleep 0.5
    done

    [ "${REBOOTED}" = "true" ]

    incus publish foo --alias foo --force
    incus image delete foo

    incus restart -f foo
    incus stop foo --force
    ! incus list | grep -q foo || false

    # Test renaming/deletion of the default profile
    ! incus profile rename default foobar || false
    ! incus profile delete default || false

    incus init testimage c1
    result="$(! incus config device override c1 root pool=bla 2>&1)"
    if ! echo "${result}" | grep "Error: Cannot update root disk device pool name"; then
        echo "Should fail device override because root disk device storage pool cannot be changed."
        false
    fi

    incus rm -f c1

    # Should fail to override root device storage pool when the new pool does not exist.
    ! incus init testimage c1 -d root,pool=bla || false

    # Should succeed in overriding root device storage pool when the pool does exist and the override occurs at create time.
    incus storage create bla dir
    incus init testimage c1 -d root,pool=bla
    incus config show c1 --expanded | grep -Pz '  root:\n    path: /\n    pool: bla\n    type: disk\n'

    incus storage volume create bla vol1
    incus storage volume create bla vol2
    incus config device add c1 dev disk source=vol1 pool=bla path=/vol

    # Should not be able to override a device that is not part of a profile (i.e. has been specifically added).
    result="$(! incus config device override c1 dev source=vol2 2>&1)"
    if ! echo "${result}" | grep "Error: The device already exists"; then
        echo "Should fail because device is defined against the instance not the profile."
        false
    fi

    incus rm -f c1
    incus storage volume delete bla vol1
    incus storage volume delete bla vol2
    incus storage delete bla

    # Test rebuilding an instance with its original image.
    incus init testimage c1
    incus start c1
    incus exec c1 -- touch /data.txt
    incus stop c1
    incus rebuild testimage c1
    incus start c1
    ! incus exec c1 -- stat /data.txt || false
    incus delete c1 -f

    # Test a forced rebuild
    incus launch testimage c1
    ! incus rebuild testimage c1 || false
    incus rebuild testimage c1 --force
    incus delete c1 -f

    # Test rebuilding an instance with a new image.
    incus init c1 --empty
    incus rebuild testimage c1
    incus start c1
    incus delete c1 -f

    # Test rebuilding an instance with an empty file system.
    incus init testimage c1
    incus rebuild c1 --empty
    ! incus config show c1 | grep -q 'image.*' || false
    incus delete c1 -f

    # Test assigning an empty profile (with no root disk device) to an instance.
    incus init testimage c1
    incus profile create foo
    ! incus profile assign c1 foo || false
    incus profile delete foo
    incus delete -f c1

    # Multiple ephemeral instances delete
    incus launch testimage c1
    incus launch testimage c2
    incus launch testimage c3

    incus delete -f c1 c2 c3
    remaining_instances="$(incus list --format csv)"
    [ -z "${remaining_instances}" ]

    # Test autorestart mechanism
    incus launch testimage c1 -c boot.autorestart=true

    # 10 restarts in 1 minute should disable auto-restart.
    # We minimize the sleep time to ensure that we manage the restarts inside the deadline
    for _ in $(seq 10); do
        retries=0
        PID=""
        while [ -z "$PID" ] && [ "$retries" -lt 5 ]; do
            PID=$(incus info c1 | awk '/^PID/ {print $2}')
            if [ -z "$PID" ]; then
                sleep 1
                retries=$((retries + 1))
            fi
            echo "PID = $PID, retries = $retries"
        done

        if [ -n "$PID" ]; then
            kill -9 "$PID"
        else
            echo "Failed to get PID after 5 retries"
            break
        fi
        sleep 1
    done

    # The 10th restart should've occurred. Wait for state to be reflected, and verified as running
    sleep 2
    [ "$(incus list -cs -fcsv c1)" = "RUNNING" ] || false

    PID=$(incus info c1 | awk '/^PID/ {print $2}')
    kill -9 "${PID}"
    sleep 3

    [ "$(incus list -cs -fcsv c1)" = "STOPPED" ] || false
    incus delete -f c1
}