summary: Check that a broken kernel snap automatically rolls itself back

details: |
  Check the trigger for a broken kernel installation (there are 7 different
  kernel corruptions) adds an install change error and that the initial kernel revision
  remains installed and we don't have leftover bootenv.

# TODO:UC20: write equivalent test for base snap failover
systems: [ubuntu-2*]

environment:
  PROBLEM_TYPE/crash: crash
  PROBLEM_TYPE/empty: empty
  PROBLEM_TYPE/zeros: zeros
  PROBLEM_TYPE/nolink: nolink
  # bad-text produces a bad systemd efi stub (.text section in the binary)
  PROBLEM_TYPE/bad_text: bad-text
  PROBLEM_TYPE/bad_linux: bad-linux
  PROBLEM_TYPE/bad_initrd: bad-initrd

prepare: |
  echo "Build a broken kernel snap where the initramfs panic's"

  # shellcheck source=tests/lib/prepare.sh
  . "$TESTSLIB/prepare.sh"

  # use the kernel snap from the nested VM and repack it so that it panic's in
  # the initramfs and rolls back to the original version
  # TODO:UC20: this doesn't work for some reason when we copy it from the 
  #            running system, we should look into that, but for now just 
  #            re-download the snap
  version="$(tests.nested show version)"
  snap download pc-kernel --channel="$version/beta" --basename=pc-kernel
  case "$PROBLEM_TYPE" in
      crash)
          if [ "$version" -le 22 ]; then
            uc20_build_initramfs_kernel_snap pc-kernel.snap "$PWD" --inject-kernel-panic-in-initramfs
          else
            uc24_build_initramfs_kernel_snap pc-kernel.snap "$PWD" --inject-kernel-panic-in-initramfs
          fi
          ;;
      nolink)
          # Failure will be forced by removing the try-kernel.efi link later,
          # do not modify the downloaded kernel (just change name so it is picked
          # up by the move command later)
          mv pc-kernel.snap pc-kernel_.snap
          ;;
      *)
          uc20_build_corrupt_kernel_snap pc-kernel.snap "$PWD" --"$PROBLEM_TYPE"
          ;;
  esac
  mv pc-kernel_*.snap panicking-initramfs-kernel.snap

execute: |
  echo "Copy the broken initramfs kernel snap to the UC20 VM"
  remote.push panicking-initramfs-kernel.snap

  echo "Wait for snapd to be available"
  remote.wait-for snap-command

  echo "Wait for snapd to be seeded"
  remote.exec sudo snap wait system seed.loaded

  # Get the current revision of the kernel snap
  # TODO:UC20: enable for pi-kernel, etc. when used with external systems
  startRevision=$(remote.exec sudo snap list pc-kernel | grep pc-kernel | awk '{print $3}')
  if [ -z "${startRevision}" ]; then
    echo "missing pc-kernel revision"
    exit 1
  fi

  boot_id="$( tests.nested boot-id )"

  echo "Install it and get the ID for the change"
  REMOTE_CHG_ID=$(remote.exec sudo snap install --dangerous panicking-initramfs-kernel.snap --no-wait)
  if [ "$PROBLEM_TYPE" = "nolink" ]; then
      remote.exec "retry --wait 1 -n 100 sh -c 'sudo rm /run/mnt/ubuntu-boot/EFI/ubuntu/try-kernel.efi'"
  fi

  # wait for a reboot. Note that failure is immediate for kernel.efi
  # files with invalid PE headers in the FDE case, as snapd is not
  # able to unseal the encryption key in that case ("cannot compute
  # load verification event").
  if [ "$PROBLEM_TYPE" != "empty" ] && [ "$PROBLEM_TYPE" != "zeros" ]; then
      remote.wait-for reboot "${boot_id}"
  fi

  # Wait for the change to finish - note it will exit with non-zero since the 
  # change will fail, so don't let that kill the test here
  if remote.exec sudo snap watch "${REMOTE_CHG_ID}"; then 
    echo "remote snap change ${REMOTE_CHG_ID} for broken kernel snap refresh should have failed!"
    exit 1
  fi

  echo "Check that the refresh failed"
  remote.exec sudo snap changes | grep "${REMOTE_CHG_ID}" | MATCH Error
  case "$PROBLEM_TYPE" in
      empty)
          remote.exec sudo snap tasks "${REMOTE_CHG_ID}" | MATCH "cannot decode image: EOF"
          ;;
      zeros)
          remote.exec sudo snap tasks "${REMOTE_CHG_ID}" | MATCH "cannot measure secure boot event:"
          ;;
      *)
          remote.exec sudo snap tasks "${REMOTE_CHG_ID}" | MATCH "cannot finish .* installation, there was a rollback across reboot"
          ;;
  esac

  echo "Check We should be on the same revision of the kernel snap"
  if [ "$(remote.exec sudo snap list pc-kernel | grep pc-kernel | awk '{print $3}')" != "${startRevision}" ]; then
    echo "pc-kernel is on the wrong revision"
    exit 1
  fi

  echo "Check we don't have leftover bootenv"
  remote.exec sudo snap debug boot-vars --uc20 | MATCH '^kernel_status=$'

  echo "Check that we don't have extra assets in the ubuntu-boot dir and that the currently enabled kernel is the original kernel"

  # kernel.efi symlink should point to the original kernel
  remote.exec readlink /run/mnt/ubuntu-boot/EFI/ubuntu/kernel.efi | MATCH "pc-kernel_${startRevision}.snap/kernel.efi"
  # should still have pc-kernel_$rev.snap dir
  remote.exec test -d "/run/mnt/ubuntu-boot/EFI/ubuntu/pc-kernel_${startRevision}.snap"
  if [ "$(remote.exec ls /run/mnt/ubuntu-boot/EFI/ubuntu/pc-kernel_*.snap | wc -l)" != 1 ]; then
    echo "Extra leftover pc-kernel assets in ubuntu-boot:"
    remote.exec ls /run/mnt/ubuntu-boot/EFI/ubuntu/pc-kernel_*.snap
    exit 1
  fi
