1
0
mirror of https://github.com/lxc/incus.git synced 2026-02-05 09:46:19 +01:00
Files
incus/test/suites/basic.sh
Stuart Espey 9e9899892b tests/basic_usage: Improve auto-restart test loop
The loop was problematic, it took a long time, and was sensitive to
drivers which require longer to re-activate after a kill

The core issue was that if we use >4s then we get good reliability, but
we can exceed the 60s timeout window, and thus fail the loop

The solution is to try to wait for the PID to be available, rather than
hoping it it is available after we wait for a fixed time.

This actually results in faster testing, and should be more reliable

Signed-off-by: Stuart Espey <stuart.espey@mactrix.com>
2025-08-12 14:53:37 -04:00

723 lines
28 KiB
Bash

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}"
LINSTOR_PREFIX_OVERRIDE=incus2-volume- 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
}