summary: Check basic core20 and later system functionality

details: |
    Verify some basic functionalities in ubuntu core (>=20): system
    snaps are present, the system is fully seeded, install a simple
    snap, check boot configuration, symlinks in /var/lib/snapd/snaps,
    snap recovery, loop devices and apparmor after a reboot works
    properly

systems:
  - -ubuntu-core-16-*
  - -ubuntu-core-18-*

execute: |
    case "$SPREAD_SYSTEM" in
      ubuntu-core-24-*)
        base_snap=core24
        run_mnt_pts=()
        ;;
      ubuntu-core-22-*)
        base_snap=core22
        run_mnt_pts=(/run/mnt/base /run/mnt/kernel)
        ;;
      ubuntu-core-20-*)
        base_snap=core20
        run_mnt_pts=(/run/mnt/base /run/mnt/kernel)
        ;;
      *)
        echo "Unsupported ubuntu core system, add missing case here"
        exit 1
    esac
    echo "Check that the system snaps are there"
    snap list "${base_snap}"
    snap list snapd
    if snap list core; then
        echo "The old core snap is installed but should not"
        exit 1
    fi

    echo "Ensure that the system is fully seeded"
    snap changes | MATCH "Done.*Initialize system state"

    echo "Check that a simple shell snap"
    SHELL_SNAP="test-snapd-sh-${base_snap}"
    snap install "$SHELL_SNAP"
    "${SHELL_SNAP}.sh" -c 'echo hello' | MATCH hello

    if python3 -m json.tool < /var/lib/snapd/system-key | grep '"build-id": ""'; then
        echo "The build-id of snapd must not be empty."
        exit 1
    fi

    echo "Ensure passwd/group is available for snaps"
    "${SHELL_SNAP}.sh" -c 'cat /var/lib/extrausers/passwd' | MATCH test

    # rpi devices don't use grub
    if not snap list pi-kernel &>/dev/null; then
        echo "Ensure extracted kernel.efi exists"
        kernel_name="$(snaps.name kernel)"
        for kernel in /boot/grub/"$kernel_name"*/kernel.efi; do
          test -e "${kernel}"
        done

        echo "Ensure kernel.efi is a symlink"
        test -L /boot/grub/kernel.efi

        echo "Ensure we are using managed boot assets"
        MATCH '# Snapd-Boot-Config-Edition: [0-9]+' < /boot/grub/grub.cfg
        MATCH '# Snapd-Boot-Config-Edition: [0-9]+' < /run/mnt/ubuntu-seed/EFI/ubuntu/grub.cfg
    else
        echo "Ensure extracted {kernel,initrd}.img exists"
        test -e /run/mnt/ubuntu-seed/systems/*/kernel/kernel.img
        test -e /run/mnt/ubuntu-seed/systems/*/kernel/initrd.img
    fi

    echo "Ensure that model was written to ubuntu-boot"
    test -e /run/mnt/ubuntu-boot/device/model

    # ensure that our the-tool (and thus our snap-bootstrap ran)
    # for external backend the initramfs is not rebuilt
    echo "Check that we booted with the rebuilt initramfs in the kernel snap"
    if [ "$SPREAD_BACKEND" != "external" ] && [ "$SPREAD_BACKEND" != "testflinger" ]; then
        test -e /writable/system-data/the-tool-ran
    fi

    # ensure we handled cloud-init, either we have:
    # a) cloud init is disabled
    # b) there was a cloud.cfg.d override (e.g. MAAS), then we must have more
    #    files in writable than in the core20 snap. The core20 content and the
    #    extra config will be merged
    test -e /writable/system-data/etc/cloud/cloud-init.disabled || [ "$(find /writable/system-data/etc/cloud/cloud.cfg.d/ | wc -l)" -gt "$(find /snap/"${base_snap}"/current/etc/cloud/cloud.cfg.d/ | wc -l)" ]

    # ensure that we have no symlinks from /var/lib/snapd/snaps to
    # /var/lib/snapd/seed
    for sn in /var/lib/snapd/snaps/*.snap ; do
        if [[ -L $sn ]]; then
            echo "snap $sn is a symlink but should not be"
            exit 1
        fi
    done

    # ensure that disk-mapping.json is created - we check the format of this 
    # file in unit tests and in uc20-create-partitions so we don't check 
    # anything here, just that the file exists
    test -e /var/lib/snapd/device/disk-mapping.json
    # ubuntu-save has it too
    test -e /run/mnt/ubuntu-save/device/disk-mapping.json

    # ensure the "snap recovery" command works
    MODEL="$(snap model --verbose | grep '^model' | awk '{ print $2 }')"
    BRAND_ID="$(snap model --verbose | grep '^brand-id:' | awk '{print $2}')"
    if [ "$(snap known account "username=$BRAND_ID" | grep '^validation:' | awk '{print $2}')" != "unproven" ]; then
        BRAND_ID="$BRAND_ID\*\*"
    fi
    snap recovery --unicode=never | MATCH "[0-9]+ +$BRAND_ID +$MODEL +current"

    # check that we have a boot-flags file
    test -f /run/snapd/boot-flags

    # make sure that loop devices created by snap-bootstrap initramfs-mounts for snaps are readonly
    if [ "$base_snap" = core20 ] || [ "$base_snap" = core22 ]; then
        for mount in "${run_mnt_pts[@]}"; do
            mountpoint "${mount}"
            loop="$(findmnt -o source "${mount}" -n)"
            echo "${loop}" | MATCH "/dev/loop[0-9]+"
            losetup -O ro -n --raw "${loop}" | MATCH "1"
        done
    else
        device=
        if snap list pi-kernel &>/dev/null; then
            device=pi
        else
            device=pc
        fi
        not mountpoint /run/mnt/kernel
        findmnt -n -o source /lib/firmware | MATCH /var/lib/snapd/kernel/"$device"-kernel
        findmnt -n -o source /lib/modules | MATCH /var/lib/snapd/kernel/"$device"-kernel
        # These units should have been created by the initramfs
        stat /run/systemd/system/sysroot-usr-lib-firmware.mount
        stat /run/systemd/system/sysroot-usr-lib-modules.mount
        stat /run/systemd/system/"sysroot-writable-system\x2ddata-snap-$device\x2dkernel-"*

        not mountpoint /run/mnt/base
        loop="$(findmnt -o source / -n)"
        losetup -O back-file -n --raw "$loop" | MATCH '/run/mnt/data/.*/core24_.*'
    fi

    # make sure that ubuntu-{seed,save} is mounted with appropriate flags
    for mount in /run/mnt/ubuntu-seed /run/mnt/ubuntu-save; do
        MNT_NAME="${mount##*/}"
        findmnt -T /run/mnt/ubuntu-save > "${MNT_NAME}.info"

        # print it for debug purposes before we match flags
        cat "${MNT_NAME}.info"
        MATCH nosuid < "${MNT_NAME}.info"
        MATCH noexec < "${MNT_NAME}.info"
        MATCH nodev < "${MNT_NAME}.info"
    done

    # ensure apparmor works, see LP: 2024637
    systemctl status apparmor.service

    # reboot to double check that apparmor still works after the reboot
    # (LP: 2024637)
    if [ "$SPREAD_REBOOT" = 0 ]; then
        REBOOT
    fi
