tests/images/iso-kargs.sh
#!/bin/bash
set -xeuo pipefail
PS4='${LINENO}: '

fatal() {
    echo "$@" >&2
    exit 1
}

digest() {
    # Ignore filename
    sha256sum "${1:--}" | awk '{print $1}'
}

grepq() {
    # Emulate grep -q without actually using it, to avoid propagating write
    # errors to the writer after a match, which would cause problems with
    # -o pipefail
    grep "$@" > /dev/null
}

iso=$1; shift
iso=$(realpath "${iso}")

s390x=
if [[ "${iso}" == *.s390x.* ]]; then
    s390x=1
    if [[ "${iso}" == *2023-03.s390x.* ]]; then
        echo "Skipped; kargs not supported on s390x before July 2023"
        exit 0
    fi
fi

tmpd=$(mktemp -d)
trap 'rm -rf "${tmpd}"' EXIT
cd "${tmpd}"

if [ "${iso%.xz}" != "${iso}" ]; then
    xz -dc "${iso}" > test.iso
else
    cp --reflink=auto "${iso}" "test.iso"
fi
iso=test.iso
out_iso="${iso}.out"

# Sanity-check the ISO doesn't somehow already have the karg we're testing with.
if coreos-installer iso kargs show "${iso}" | grepq foobar; then
    fatal "Unexpected foobar karg in iso kargs"
fi

orig_hash=$(digest "${iso}")

# Stream modification to stdout.
stdout_hash=$(coreos-installer iso kargs modify -a foobar=oldval -a dodo -o - "${iso}" | tee "${out_iso}" | digest)
coreos-installer iso kargs show "${out_iso}" | grepq 'foobar=oldval dodo'
coreos-installer iso kargs modify -d foobar=oldval -d dodo -o - "${out_iso}" > "${iso}"
if coreos-installer iso kargs show "${iso}" | grepq 'foobar'; then
    fatal "Unexpected foobar karg in iso kargs"
fi
hash=$(digest "${iso}")
if [ "${orig_hash}" != "${hash}" ]; then
    fatal "Hash doesn't match original hash: ${hash} vs ${orig_hash}"
fi

# Test all the modification operations.
coreos-installer iso kargs modify -a foobar=oldval -a dodo "${iso}"
coreos-installer iso kargs show "${iso}" | grepq 'foobar=oldval dodo'
hash=$(digest "${iso}")
if [ "${stdout_hash}" != "${hash}" ]; then
    fatal "Streamed hash doesn't match modified hash: ${stdout_hash} vs ${hash}"
fi
rm "${out_iso}"
coreos-installer iso kargs modify -r foobar=oldval=newval "${iso}" -o "${out_iso}"
coreos-installer iso kargs show "${out_iso}" | grepq 'foobar=newval dodo'
rm "${iso}"
coreos-installer iso kargs modify -d foobar=newval -d dodo "${out_iso}" -o "${iso}"
if coreos-installer iso kargs show "${iso}" | grepq 'foobar'; then
    fatal "Unexpected foobar karg in iso kargs"
fi

hash=$(digest "${iso}")
if [ "${orig_hash}" != "${hash}" ]; then
    fatal "Hash doesn't match original hash: ${hash} vs ${orig_hash}"
fi

# Test the largest karg; we get the full area length from --header and subtract
# the default kargs size to get the size of the overflow embed area.
embed_size=$(coreos-installer dev show iso --kargs "${iso}" | jq .length)
embed_default_kargs_size=$(coreos-installer iso kargs show --default "${iso}" | wc -c)
embed_usable_size=$((${embed_size} - ${embed_default_kargs_size} - 1))

long_karg=$(printf '%*s' $((embed_usable_size)) | tr ' ' "k")
coreos-installer iso kargs modify -a "${long_karg}" "${iso}"
coreos-installer iso kargs show "${iso}" | grepq " ${long_karg}\$"
coreos-installer iso kargs reset "${iso}"
long_karg=$(printf '%*s' $((embed_usable_size + 1)) | tr ' ' "k")
if coreos-installer iso kargs modify -a "${long_karg}" "${iso}" 2>err.txt; then
    fatal "Was able to put karg longer than area?"
fi
grepq 'kargs too large for area' err.txt

# Test `reset`.
coreos-installer iso kargs modify -a foobar "${iso}"
rm "${out_iso}"
coreos-installer iso kargs reset "${iso}" -o "${out_iso}"
hash=$(digest "${out_iso}")
if [ "${orig_hash}" != "${hash}" ]; then
    fatal "Hash doesn't match original hash: ${hash} vs ${orig_hash}"
fi
coreos-installer iso kargs reset "${iso}" -o - > "${out_iso}"
hash=$(digest "${out_iso}")
if [ "${orig_hash}" != "${hash}" ]; then
    fatal "Hash doesn't match original hash: ${hash} vs ${orig_hash}"
fi
coreos-installer iso kargs reset "${iso}"
hash=$(digest "${iso}")
if [ "${orig_hash}" != "${hash}" ]; then
    fatal "Hash doesn't match original hash: ${hash} vs ${orig_hash}"
fi

# Check modification against expected ground truth.
coreos-installer iso kargs modify -a foobar=val "${iso}"
offset=$(coreos-installer dev show iso --kargs "${iso}" | jq -r .kargs[0].offset)
length=$(coreos-installer dev show iso --kargs "${iso}" | jq -r .kargs[0].length)
expected_args="$(coreos-installer iso kargs show --default "${iso}") foobar=val"
expected_args_len="$(echo -n "${expected_args}" | wc -c)"
if [[ -n "${s390x}" ]]; then
    # There is no filler or newline character on s390x's kargs line, but null bytes:
    #    $ hexdump -C -s 138457 -n 896 builds/latest/s390x/*.iso
    #    00021cd9  69 67 6e 69 74 69 6f 6e  2e 70 6c 61 74 66 6f 72  |ignition.platfor|
    #    00021ce9  6d 2e 69 64 3d 6d 65 74  61 6c 00 00 00 00 00 00  |m.id=metal......|
    # Create temp file with kargs and nulls and compare it to bytes of the iso image:
    echo -n "${expected_args}" > cmdline.s390x
    truncate -s "${length}" cmdline.s390x
    if ! cmp -s <(dd if=${iso} skip=${offset} count=${length} bs=1 status=none) cmdline.s390x; then
        fatal "Failed to manually round-trip kargs"
    fi
else
    filler="$(printf '%*s' $((${length} - ${expected_args_len} - 1)) | tr ' ' '#')"
    if ! echo -en "${expected_args}\n${filler}" | cmp -s <(dd if=${iso} skip=${offset} count=${length} bs=1 status=none) -; then
        fatal "Failed to manually round-trip kargs"
    fi
fi

# Done
echo "Success."
