Current Dir: /snap/canonical-livepatch/418/resources/tests/

[FILE] 99-livepatch-kernel-upgrade-required.bats [ edit | delete | rename ]
[FILE] livepatch-kernel-installed.bats [ edit | delete | rename ]
[FILE] reboot-required-check.bats [ edit | delete | rename ]

Viewing: /snap/canonical-livepatch/418/resources/tests/reboot-required-check.bats

#!/usr/bin/env bats
#
# Tests for resources/reboot-required-check.
#
# Each test copies the script to a temp file and rewrites just the path
# variable assignments with sed before executing the copy. A stub
# canonical-livepatch logs each invocation's first argument to
# "$WORKDIR/calls" and its output/exit code are driven by env vars.

setup() {
    ORIG_SCRIPT="${BATS_TEST_DIRNAME}/../reboot-required-check"
    WORKDIR="$(mktemp -d)"
    SCRIPT="${WORKDIR}/reboot-required-check"
    NOTE="${WORKDIR}/canonical-livepatch-kernel-installed"
    GRUB_CFG="${WORKDIR}/grub.cfg"
    REBOOT_REQUIRED="${WORKDIR}/reboot-required"
    REBOOT_REQUIRED_PKGS="${WORKDIR}/reboot-required.pkgs"
    LIVEPATCH_STUB="${WORKDIR}/canonical-livepatch"
    CALLS="${WORKDIR}/calls"

    sed \
        -e "s#^NOTE=.*#NOTE=\"${NOTE}\"#" \
        -e "s#^GRUB_CFG=.*#GRUB_CFG=\"${GRUB_CFG}\"#" \
        -e "s#^REBOOT_REQUIRED=.*#REBOOT_REQUIRED=\"${REBOOT_REQUIRED}\"#" \
        -e "s#^REBOOT_REQUIRED_PKGS=.*#REBOOT_REQUIRED_PKGS=\"${REBOOT_REQUIRED_PKGS}\"#" \
        -e "s#^LIVEPATCH=.*#LIVEPATCH=\"${LIVEPATCH_STUB}\"#" \
        -e "s#^TIMEOUT_SECONDS=.*#TIMEOUT_SECONDS=1#" \
        "$ORIG_SCRIPT" > "$SCRIPT"
    chmod +x "$SCRIPT"
}

teardown() {
    rm -rf "$WORKDIR"
}

# chmod-based permission tests are meaningless when run as root, since root
# bypasses DAC read/unlink checks.
skip_if_root() {
    if [ "$(id -u)" -eq 0 ]; then
        skip "permission checks are bypassed when running as root"
    fi
}

livepatch_aware_policy='{"reboot-notification-policy":"livepatch-aware"}'

# Writes a stub canonical-livepatch that logs $1 to $CALLS, then answers
# "config"/"refresh"/"kernel-upgrade-required" per env vars:
#   LP_CONFIG_OUT, LP_CONFIG_EXIT (default the aware policy, 0)
#   LP_REFRESH_EXIT (default 0)
#   LP_KUR_EXIT (default 2), LP_KUR_SLEEP (default 0, seconds to sleep first)
stub_livepatch() {
    cat > "$LIVEPATCH_STUB" <<EOF
#!/bin/sh
printf '%s\n' "\$1" >> "$CALLS"
case "\$1" in
    config)
        printf '%s' '${LP_CONFIG_OUT:-$livepatch_aware_policy}'
        exit "${LP_CONFIG_EXIT:-0}"
        ;;
    refresh)
        exit "${LP_REFRESH_EXIT:-0}"
        ;;
    kernel-upgrade-required)
        sleep "${LP_KUR_SLEEP:-0}"
        exit "${LP_KUR_EXIT:-2}"
        ;;
esac
EOF
    chmod +x "$LIVEPATCH_STUB"
}

@test "fast path: no note, no calls, no output" {
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -z "$output" ]
    [ ! -f "$CALLS" ]
}

@test "note present, no grub.cfg: note removed, no calls" {
    touch "$NOTE"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$NOTE" ]
    [ ! -f "$CALLS" ]
}

@test "note newer than grub.cfg: note kept, no calls" {
    touch "$GRUB_CFG"
    touch -d '+1 min' "$NOTE"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$NOTE" ]
    [ ! -f "$CALLS" ]
}

@test "equal mtimes count as fresh and proceed (note removed)" {
    touch "$NOTE"
    touch -r "$NOTE" "$GRUB_CFG"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$NOTE" ]
}

@test "marker absent: note removed, no snap calls" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$NOTE" ]
    [ ! -f "$CALLS" ]
}

@test "pkgs missing: marker kept, no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ ! -f "$CALLS" ]
}

@test "pkgs empty: marker kept, no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    : > "$REBOOT_REQUIRED_PKGS"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ ! -f "$CALLS" ]
}

@test "pkgs with only blank lines: marker kept, no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf '\n\n' > "$REBOOT_REQUIRED_PKGS"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
    [ ! -f "$CALLS" ]
}

@test "pkgs containing libc6: marker kept, no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'libc6\n' > "$REBOOT_REQUIRED_PKGS"
    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ ! -f "$CALLS" ]
}

@test "blank lines and duplicate kernel lines are kernel-only: both files removed on KUR exit 1" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n\nlinux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=1
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$REBOOT_REQUIRED" ]
    [ ! -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "linux-base next to kernel packages is still kernel-only: both files removed on KUR exit 1" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-base\nlinux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=1
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$REBOOT_REQUIRED" ]
    [ ! -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "linux-base alone is not a kernel install: nothing removed, no calls" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-base\n' > "$REBOOT_REQUIRED_PKGS"
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ ! -f "$CALLS" ]
}

@test "LIVEPATCH not executable: no calls, marker kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    rm -f "$LIVEPATCH_STUB"

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ ! -f "$CALLS" ]
}

@test "policy livepatch-agnostic: no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_CONFIG_OUT='{"reboot-notification-policy":"livepatch-agnostic"}'
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ "$(cat "$CALLS")" = "config" ]
}

@test "malformed policy JSON: no refresh call" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_CONFIG_OUT='{"unexpected":"json"}'
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ "$(cat "$CALLS")" = "config" ]
}

@test "checking kernel coverage line printed only when refresh is called" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=1
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    case "$output" in
        *"Livepatch: checking kernel coverage..."*) ;;
        *) false ;;
    esac
}

@test "no coverage line when policy check fails fast" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    rm -f "$LIVEPATCH_STUB"

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    case "$output" in
        *"checking kernel coverage"*) false ;;
        *) ;;
    esac
}

@test "refresh exits 1: no KUR call, marker kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_REFRESH_EXIT=1
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ "$(cat "$CALLS")" = "$(printf 'config\nrefresh')" ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "KUR exit 0: marker and pkgs kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=0
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "KUR exit 2: marker and pkgs kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=2
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "KUR exit 3: marker and pkgs kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=3
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
}

@test "KUR exit 1: pkgs and marker removed, removal line printed" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_EXIT=1
    stub_livepatch

    run "$SCRIPT"
    [ "$status" -eq 0 ]
    [ ! -f "$REBOOT_REQUIRED" ]
    [ ! -f "$REBOOT_REQUIRED_PKGS" ]
    [ "$(cat "$CALLS")" = "$(printf 'config\nrefresh\nkernel-upgrade-required')" ]
    case "$output" in
        *"Livepatch: reboot-required notice removed; the running kernel is covered by Livepatch."*) ;;
        *) false ;;
    esac
}

@test "KUR times out (124): marker kept" {
    touch "$NOTE"
    touch "$GRUB_CFG"
    touch "$REBOOT_REQUIRED"
    printf 'linux-image-6.8.0-50-generic\n' > "$REBOOT_REQUIRED_PKGS"
    LP_KUR_SLEEP=5
    stub_livepatch

    start=$(date +%s)
    run "$SCRIPT"
    elapsed=$(( $(date +%s) - start ))
    [ "$status" -eq 0 ]
    [ -f "$REBOOT_REQUIRED" ]
    [ -f "$REBOOT_REQUIRED_PKGS" ]
    [ "$elapsed" -lt 4 ]
}

@test "note cannot be removed: exits 0, no calls" {
    skip_if_root
    touch "$GRUB_CFG"
    touch -r "$GRUB_CFG" "$NOTE"
    chmod 555 "$WORKDIR"

    run "$SCRIPT"
    chmod 755 "$WORKDIR"
    [ "$status" -eq 0 ]
    [ -f "$NOTE" ]
    [ ! -f "$CALLS" ]
}

Upload File: