From e5d9077c395e09010023db36d0e6d6dac9d13241 Mon Sep 17 00:00:00 2001 From: Kyle MacLeod <kyle.macleod@windriver.com> Date: Tue, 20 Jun 2023 17:47:11 -0400 Subject: [PATCH] Update gen iso scripts for CentOS support in Debian Update existing dynamic ISO generation scripts to target the last CentOS release from within a Debian environment. Changes are applied for gen-prestaged-iso-centos.sh for support within a Debian instance. The existing Debian-targeting files (gen-bootloader-iso.sh, gen-prestaged-iso.sh) are updated for minor fixes in logging, consistency, and overall code structure for ease of support and maintenance. This commit aligns recent changes of gen-bootloader-iso.sh into gen-bootloader-iso-centos.sh, for easier support going forward. And finally, unit tests are added for the gen-prestaged-iso* scripts. The unit tests use shunit2 framework, which is dynamically pulled in as required to run the tests. At this point, tests are only run manually; they are not part of the loadbuild. Unit tests may be added for gen-bootloader-iso.sh in a further commit; however, this is of lesser priority since this script is employed frequently on subcloud remote installations. Test Plan PASS: - Test gen-prestaged-iso.sh for generating a prestaged ISO targeting a local Debian install. An ISO is generated and a full subcloud deployment is successful - Test gen-prestaged-iso-centos.sh for generating a prestaged ISO targeting a local CentOS install. An ISO is generated and a full subcloud deployment is successful - Run the two new unit tests, gen-prestaged-iso-test.sh, and gen-prestaged-iso-centos-test.sh, both in standalone and via run-tests.sh. Verify with both an empty and populated input directory. - Verify full remote/redfish Debian subcloud install and deployment. Targeted at gen-bootloader-iso.sh changes. - Verify full remote/redfish CentOS subcloud install and deployment. Targeted at gen-bootloader-iso-centos.sh changes. Story: 2010611 Task: 48267 Depends-On: https://review.opendev.org/c/starlingx/metal/+/886662 Signed-off-by: Kyle MacLeod <kyle.macleod@windriver.com> Change-Id: I31e76ed107f589b1196320b3c8d7243fb15d3491 --- .../scripts/gen-bootloader-iso-centos.sh | 925 +++++++++--------- .../scripts/gen-bootloader-iso.sh | 131 +-- .../scripts/gen-prestaged-iso-centos.sh | 351 +++---- .../scripts/gen-prestaged-iso.sh | 319 +++--- .../scripts/stx-iso-utils-centos.sh | 187 ++-- .../platform-util/scripts/stx-iso-utils.sh | 22 +- .../platform-util/scripts/test/.gitignore | 3 + .../platform-util/scripts/test/README.md | 24 + .../test/gen-prestaged-iso-centos-test.sh | 300 ++++++ .../scripts/test/gen-prestaged-iso-test.sh | 302 ++++++ .../platform-util/scripts/test/run-tests.sh | 24 + .../scripts/test/shunit2_helper.sh | 68 ++ 12 files changed, 1760 insertions(+), 896 deletions(-) create mode 100644 utilities/platform-util/scripts/test/.gitignore create mode 100644 utilities/platform-util/scripts/test/README.md create mode 100755 utilities/platform-util/scripts/test/gen-prestaged-iso-centos-test.sh create mode 100755 utilities/platform-util/scripts/test/gen-prestaged-iso-test.sh create mode 100755 utilities/platform-util/scripts/test/run-tests.sh create mode 100644 utilities/platform-util/scripts/test/shunit2_helper.sh diff --git a/utilities/platform-util/scripts/gen-bootloader-iso-centos.sh b/utilities/platform-util/scripts/gen-bootloader-iso-centos.sh index 76ed431d..ecda8d8f 100755 --- a/utilities/platform-util/scripts/gen-bootloader-iso-centos.sh +++ b/utilities/platform-util/scripts/gen-bootloader-iso-centos.sh @@ -1,4 +1,5 @@ #!/bin/bash +# vim: filetype=sh shiftwidth=4 expandtab # # Copyright (c) 2020-2023 Wind River Systems, Inc. # @@ -12,43 +13,119 @@ # under an http/https served directory # # +readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") +readonly SCRIPTNAME=$(basename "$0") # Source shared utility functions -source "$(dirname "$0")"/stx-iso-utils-centos.sh +# shellcheck disable=SC1090 # ignore source warning +source "$SCRIPTDIR"/stx-iso-utils-centos.sh -declare LOG_TAG=$(basename $0) +ADDON= +BASE_URL= +BOOT_ARGS_COMMON= +BOOT_GATEWAY= +BOOT_HOSTNAME= +BOOT_INTERFACE= +BOOT_IP= +BOOT_NETMASK= +CLEAN_NODE_DIR="no" +CLEAN_SHARED_DIR="no" +DEFAULT_GRUB_ENTRY= +DEFAULT_SYSLINUX_ENTRY= +DELETE="no" +GRUB_TIMEOUT=-1 +INPUT_ISO= +INSTALL_TYPE= +ISO_VERSION= +KS_NODETYPE= +LOCK_FILE=/var/run/.gen-bootloader-iso.lock +LOCK_TMOUT=600 # Wait up to 10 minutes, by default +LOG_TAG=$SCRIPTNAME +NODE_ID= +NODE_URL= +OUTPUT_ISO= +PATCHES_FROM_HOST="yes" +SCRATCH_DIR=${SCRATCH_DIR:-/scratch} +TIMEOUT=0 +UPDATE_TIMEOUT="no" +VERBOSE=${VERBOSE:-} +VERBOSE_LOG_DIR=/var/log/dcmanager/miniboot +VERBOSE_OVERRIDE_FILE=/tmp/gen-bootloader-verbose # turn on verbose if this file is present +WWW_ROOT_DIR= -function log_error { - logger -i -s -t ${LOG_TAG} -- "$@" +declare -a PARAMS + +# Initialized via initialize_and_lock: +BUILDDIR= +NODE_DIR= +NODE_DIR_BASE= +VERBOSE_RSYNC= +WORKDIR= + +# Initialized by stx-iso-utils-centos.sh:mount_efiboot_img +EFI_MOUNT= + +# Set this to a directory path containing kickstart *.cfg script(s) for testing: +KICKSTART_OVERRIDE_DIR=${KICKSTART_OVERRIDE_DIR:-/var/miniboot/kickstart-override-centos} + +function log_verbose { + if [ -n "$VERBOSE" ]; then + echo "$@" + fi } -declare ADDON= -declare BASE_URL= -declare BOOT_GATEWAY= -declare BOOT_HOSTNAME= -declare BOOT_INTERFACE= -declare BOOT_IP= -declare BOOT_NETMASK= -declare CLEAN_NODE_DIR="no" -declare CLEAN_SHARED_DIR="no" -declare DEFAULT_GRUB_ENTRY= -declare DEFAULT_LABEL= -declare DEFAULT_SYSLINUX_ENTRY= -declare DELETE="no" -declare GRUB_TIMEOUT=-1 -declare INPUT_ISO= -declare ISO_VERSION= -declare KS_NODETYPE= -declare -i LOCK_TMOUT=600 # Wait up to 10 minutes, by default -declare NODE_ID= -declare ORIG_PWD=$PWD -declare OUTPUT_ISO= -declare -a PARAMS -declare PATCHES_FROM_HOST="yes" -declare -i TIMEOUT=0 -declare UPDATE_TIMEOUT="no" -declare WORKDIR= -declare WWW_ROOT_DIR= +function log_info { + echo "$@" +} + +function log_error { + logger -i -s -t "${LOG_TAG}" -- "ERROR: $*" +} + +function log_warn { + logger -i -s -t "${LOG_TAG}" -- "WARN: $*" +} + +function fatal_error { + logger -i -s -t "${LOG_TAG}" -- "FATAL: $*" + exit 1 +} + +function check_rc_exit { + local rc=$1 + shift + if [ "$rc" -ne 0 ]; then + logger -i -s -t "${LOG_TAG}" -- "FATAL: $* [exit: $rc]" + exit "$rc" + fi +} + +function get_os { + local os + os=$(awk -F '=' '/^ID=/ { print $2; }' /etc/os-release) + case "$os" in + *debian*) + echo debian + ;; + *centos*) + echo centos + ;; + *) + echo "$os" + ;; + esac +} + +function get_path_size { + local path=$1 + du -hs "$path" | awk '{print $1}' +} + +function log_path_size { + local path=$1 + local msg=$2 + log_info "$msg: $(get_path_size "$path")" +} function usage { cat <<ENDUSAGE @@ -118,155 +195,240 @@ EOF ENDUSAGE } -# -# Parse cmdline arguments -# -LONGOPTS="input:,addon:,param:,default-boot:,timeout:,lock-timeout:,patches-from-iso" -LONGOPTS="${LONGOPTS},base-url:,www-root:,id:,delete" -LONGOPTS="${LONGOPTS},boot-gateway:,boot-hostname:,boot-interface:,boot-ip:,boot-netmask:" -LONGOPTS="${LONGOPTS},help" - -OPTS=$(getopt -o h --long "${LONGOPTS}" --name "$0" -- "$@") - -if [ $? -ne 0 ]; then - usage - exit 1 -fi - -eval set -- "${OPTS}" - -while :; do - case "$1" in - --input) - INPUT_ISO=$2 - shift 2 - ;; - --addon) - ADDON=$2 - shift 2 - ;; - --boot-gateway) - BOOT_GATEWAY=$2 - shift 2 - ;; - --boot-hostname) - BOOT_HOSTNAME=$2 - shift 2 - ;; - --boot-interface) - BOOT_INTERFACE=$2 - shift 2 - ;; - --boot-ip) - BOOT_IP=$2 - shift 2 - ;; - --boot-netmask) - BOOT_NETMASK=$2 - shift 2 - ;; - --param) - PARAMS+=($2) - shift 2 - ;; - --default-boot) - DEFAULT_LABEL=$2 - shift 2 - - case ${DEFAULT_LABEL} in - 0) - DEFAULT_SYSLINUX_ENTRY=0 - DEFAULT_GRUB_ENTRY="serial" - KS_NODETYPE='controller' - ;; - 1) - DEFAULT_SYSLINUX_ENTRY=1 - DEFAULT_GRUB_ENTRY="graphical" - KS_NODETYPE='controller' - ;; - 2) - DEFAULT_SYSLINUX_ENTRY=0 - DEFAULT_GRUB_ENTRY="serial" - KS_NODETYPE='smallsystem' - ;; - 3) - DEFAULT_SYSLINUX_ENTRY=1 - DEFAULT_GRUB_ENTRY="graphical" - KS_NODETYPE='smallsystem' - ;; - 4) - DEFAULT_SYSLINUX_ENTRY=0 - DEFAULT_GRUB_ENTRY="serial" - KS_NODETYPE='smallsystem_lowlatency' - ;; - 5) - DEFAULT_SYSLINUX_ENTRY=1 - DEFAULT_GRUB_ENTRY="graphical" - KS_NODETYPE='smallsystem_lowlatency' - ;; - *) - log_error "Invalid default boot menu option: ${DEFAULT_LABEL}" - usage - exit 1 - ;; - esac - ;; - --timeout) - let -i timeout_arg=$2 - shift 2 - - if [ ${timeout_arg} -gt 0 ]; then - let -i TIMEOUT=${timeout_arg}*10 - GRUB_TIMEOUT=${timeout_arg} - elif [ ${timeout_arg} -eq 0 ]; then - GRUB_TIMEOUT=0.001 - fi - - UPDATE_TIMEOUT="yes" - ;; - --www-root) - WWW_ROOT_DIR=$2 - shift 2 - ;; - --base-url) - BASE_URL=$2 - shift 2 - ;; - --id) - NODE_ID=$2 - shift 2 - ;; - --lock-timeout) - LOCK_TMOUT=$2 - shift 2 - if [ $LOCK_TMOUT -le 0 ]; then - echo "Lock timeout must be greater than 0" >&2 - exit 1 - fi - ;; - --delete) - DELETE="yes" - shift - ;; - --patches-from-iso) - PATCHES_FROM_HOST="no" - shift - ;; - --) - shift - break - ;; - *) - usage - exit 1 - ;; - esac -done - # # Functions # +function parse_arguments { + # Parse cmdline arguments + local longopts opts + longopts="input:,addon:,param:,default-boot:,timeout:,lock-timeout:,patches-from-iso" + longopts="${longopts},base-url:,www-root:,id:,delete" + longopts="${longopts},boot-gateway:,boot-hostname:,boot-interface:,boot-ip:,boot-netmask:" + longopts="${longopts},verbose,help" + + opts=$(getopt -o h --long "${longopts}" --name "$0" -- "$@") + # shellcheck disable=SC2181 # prefer to check exit code: + if [ $? -ne 0 ]; then + usage + exit 1 + fi + + eval set -- "${opts}" + + while :; do + case "$1" in + --input) + INPUT_ISO=$2 + shift 2 + ;; + --addon) + ADDON=$2 + shift 2 + ;; + --boot-gateway) + BOOT_GATEWAY=$2 + shift 2 + ;; + --boot-hostname) + BOOT_HOSTNAME=$2 + shift 2 + ;; + --boot-interface) + BOOT_INTERFACE=$2 + shift 2 + ;; + --boot-ip) + BOOT_IP=$2 + shift 2 + ;; + --boot-netmask) + BOOT_NETMASK=$2 + shift 2 + ;; + --param) + PARAMS+=($2) + shift 2 + ;; + --default-boot) + INSTALL_TYPE=$2 + shift 2 + case ${INSTALL_TYPE} in + 0) + DEFAULT_SYSLINUX_ENTRY=0 + DEFAULT_GRUB_ENTRY=serial + KS_NODETYPE='controller' + ;; + 1) + DEFAULT_SYSLINUX_ENTRY=1 + DEFAULT_GRUB_ENTRY=graphical + KS_NODETYPE='controller' + ;; + 2) + DEFAULT_SYSLINUX_ENTRY=0 + DEFAULT_GRUB_ENTRY=serial + KS_NODETYPE='smallsystem' + ;; + 3) + DEFAULT_SYSLINUX_ENTRY=1 + DEFAULT_GRUB_ENTRY=graphical + KS_NODETYPE='smallsystem' + ;; + 4) + DEFAULT_SYSLINUX_ENTRY=0 + DEFAULT_GRUB_ENTRY=serial + KS_NODETYPE='smallsystem_lowlatency' + ;; + 5) + DEFAULT_SYSLINUX_ENTRY=1 + DEFAULT_GRUB_ENTRY=graphical + KS_NODETYPE='smallsystem_lowlatency' + ;; + *) + log_error "Invalid default boot menu option: ${INSTALL_TYPE}" + usage + exit 1 + ;; + esac + ;; + --timeout) + local -i timeout_arg=$2 + shift 2 + if [ ${timeout_arg} -gt 0 ]; then + TIMEOUT=$(( timeout_arg * 10 )) + GRUB_TIMEOUT=${timeout_arg} + elif [ ${timeout_arg} -eq 0 ]; then + GRUB_TIMEOUT=0.001 + fi + UPDATE_TIMEOUT="yes" + ;; + --www-root) + WWW_ROOT_DIR=$2 + shift 2 + ;; + --base-url) + BASE_URL=$2 + shift 2 + ;; + --id) + NODE_ID=$2 + shift 2 + ;; + --lock-timeout) + local -i LOCK_TMOUT=$2 + shift 2 + if [ "${LOCK_TMOUT}" -le 0 ]; then + echo "Lock timeout must be greater than 0" >&2 + exit 1 + fi + ;; + --delete) + DELETE="yes" + shift + ;; + --patches-from-iso) + PATCHES_FROM_HOST="no" + shift + ;; + --verbose) + VERBOSE=1 + shift + ;; + --) + shift + break + ;; + *) + usage + exit 1 + ;; + esac + done +} + +function get_lock { + # Grab the lock, to protect against simultaneous execution + # Open $LOCK_FILE for reading, with assigned file handle 200 + exec 200>${LOCK_FILE} + flock -w "${LOCK_TMOUT}" 200 + check_rc_exit $? "Failed waiting for lock: ${LOCK_FILE}" +} + +function initialize_and_lock { + check_requirements + + # Check mandatory parameters + check_required_param "--id" "${NODE_ID}" + check_required_param "--www-root" "${WWW_ROOT_DIR}" + [ -d "${WWW_ROOT_DIR}" ] || fatal_error "Root directory ${WWW_ROOT_DIR} does not exist" + + [ -f "${VERBOSE_OVERRIDE_FILE}" ] && VERBOSE=1 + if [ -n "${VERBOSE}" ]; then + VERBOSE_RSYNC="--verbose" + + # log all output to file + if [ ! -d "$(dirname "${VERBOSE_LOG_DIR}")" ]; then + # For testing: the base directory does not exist - use /tmp instead + VERBOSE_LOG_DIR=/tmp/miniboot + fi + [ -d "${VERBOSE_LOG_DIR}" ] || mkdir -p "${VERBOSE_LOG_DIR}" + local logfile="${VERBOSE_LOG_DIR}/gen-bootloader-iso-centos-${NODE_ID}.log" + [ -f "${logfile}" ] && rm -f "${logfile}" + touch "${logfile}" + echo "Verbose: logging output to ${logfile}" + echo "$(date) Starting $0" + printenv >> "${logfile}" + exec > >(tee --append "${logfile}") 2>&1 + fi + + # Initialize dynamic variables + NODE_DIR_BASE="${WWW_ROOT_DIR}/nodes" + NODE_DIR="${NODE_DIR_BASE}/${NODE_ID}" + SHARED_DIR="${WWW_ROOT_DIR}/shared" + + if [ ! -d "$SCRATCH_DIR" ]; then + log_warn "SCRATCH_DIR does not exist, using /tmp" + SCRATCH_DIR=/tmp + fi + + get_lock + + # Check for deletion + if [ ${DELETE} = "yes" ]; then + handle_delete + exit 0 + fi + + # Handle extraction and setup + check_required_param "--input" "${INPUT_ISO}" + check_required_param "--default-boot" "${DEFAULT_GRUB_ENTRY}" + check_required_param "--base-url" "${BASE_URL}" + check_required_param "--boot-ip" "${BOOT_IP}" + check_required_param "--boot-interface" "${BOOT_INTERFACE}" + + NODE_URL="${BASE_URL%\/}/nodes/${NODE_ID}" + + if [ ! -f "${INPUT_ISO}" ]; then + fatal_error "Input file does not exist: ${INPUT_ISO}" + fi + if [ -d "${NODE_DIR}" ]; then + fatal_error "Output dir already exists: ${NODE_DIR}" + fi + + # Run cleanup on any exit + trap cleanup_on_exit EXIT + + BUILDDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_build_XXXXXX) + if [ -z "${BUILDDIR}" ] || [ ! -d "${BUILDDIR}" ]; then + fatal_error "Failed to create builddir: ${BUILDDIR}" + fi + + WORKDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_workdir_XXXXXX) + if [ -z "${WORKDIR}" ] || [ ! -d "${WORKDIR}" ]; then + fatal_error "Failed to create WORKDIR directory: $WORKDIR" + fi +} + function generate_boot_cfg { local isodir=$1 @@ -277,14 +439,16 @@ function generate_boot_cfg { local KS_URL="${NODE_URL}/miniboot_${KS_NODETYPE}.cfg" local BOOT_IP_ARG="${BOOT_IP}::${BOOT_GATEWAY}:${BOOT_NETMASK}:${BOOT_HOSTNAME}:${BOOT_INTERFACE}:none" - local COMMON_ARGS="inst.text inst.gpt boot_device=sda rootfs_device=sda" - COMMON_ARGS="${COMMON_ARGS} biosdevname=0 usbcore.autosuspend=-1" - COMMON_ARGS="${COMMON_ARGS} security_profile=standard user_namespace.enable=1" - COMMON_ARGS="${COMMON_ARGS} inst.repo=${NODE_URL} inst.stage2=${NODE_URL} inst.ks=${KS_URL}" - COMMON_ARGS="${COMMON_ARGS} ip=${BOOT_IP_ARG}" + BOOT_ARGS_COMMON="inst.text inst.gpt boot_device=sda rootfs_device=sda" + BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} biosdevname=0 usbcore.autosuspend=-1" + BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} security_profile=standard user_namespace.enable=1" + BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} inst.repo=${NODE_URL} inst.stage2=${NODE_URL} inst.ks=${KS_URL}" + BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} ip=${BOOT_IP_ARG}" - for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do - cat <<EOF > ${f} + log_info "Using boot parameters: ${BOOT_ARGS_COMMON}" + log_verbose "Generating isolinux.cfg/syslinux.cfg, default: $DEFAULT_SYSLINUX_ENTRY, timeout: $TIMEOUT" + for f in "${isodir}/isolinux.cfg" "${isodir}/syslinux.cfg"; do + cat <<EOF > "${f}" display splash.cfg timeout ${TIMEOUT} F1 help.txt @@ -304,49 +468,54 @@ menu begin menu label Serial Console kernel vmlinuz initrd initrd.img - append rootwait ${COMMON_ARGS} console=ttyS0,115200 serial + append rootwait ${BOOT_ARGS_COMMON} console=ttyS0,115200 serial # Graphical Console submenu label 1 menu label Graphical Console kernel vmlinuz initrd initrd.img - append rootwait ${COMMON_ARGS} console=tty0 + append rootwait ${BOOT_ARGS_COMMON} console=tty0 menu end EOF done - for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do - cat <<EOF > ${f} + log_verbose "Generating grub.cfg, install_type: ${INSTALL_TYPE}, default: ${DEFAULT_GRUB_ENTRY}, timeout: ${GRUB_TIMEOUT}" + for f in "${isodir}/EFI/BOOT/grub.cfg" "${EFI_MOUNT}/EFI/BOOT/grub.cfg"; do + cat <<EOF > "${f}" default=${DEFAULT_GRUB_ENTRY} timeout=${GRUB_TIMEOUT} search --no-floppy --set=root -l 'oe_iso_boot' -menuentry "${NODE_ID}" { +menuentry "CentOS Miniboot Install ${NODE_ID}" { echo " " } menuentry 'Serial Console' --id=serial { - linuxefi /vmlinuz ${COMMON_ARGS} console=ttyS0,115200 serial + linuxefi /vmlinuz ${BOOT_ARGS_COMMON} console=ttyS0,115200 serial initrdefi /initrd.img } menuentry 'Graphical Console' --id=graphical { - linuxefi /vmlinuz ${COMMON_ARGS} console=tty0 + linuxefi /vmlinuz ${BOOT_ARGS_COMMON} console=tty0 initrdefi /initrd.img } EOF - done } -function cleanup { +function cleanup_on_exit { + # This is invoked from the trap handler. + # The last exit code is used to determine if we are exiting + # in failed state (non-zero exit), in which case we do the + # full cleanup. Disable the warning here since we are + # invoked as a trap handler + # shellcheck disable=SC2181 # Check exit code directly... if [ $? -ne 0 ]; then - # Clean up from failure + log_info "Cleanup on failure" handle_delete fi - common_cleanup } @@ -356,21 +525,23 @@ function check_requirements { function handle_delete { # Remove node-specific files - if [ -d ${NODE_DIR} ]; then - rm -rf ${NODE_DIR} + if [ -d "${NODE_DIR}" ]; then + rm -rf "${NODE_DIR}" fi # If there are no more nodes, cleanup everything else - if [ $(ls -A ${NODE_DIR_BASE} 2>/dev/null | wc -l) = 0 ]; then - if [ -d ${NODE_DIR_BASE} ]; then - rmdir ${NODE_DIR_BASE} + # shellcheck disable=SC2012 + if [ "$(ls -A "${NODE_DIR_BASE}" 2>/dev/null | wc -l)" = 0 ]; then + if [ -d "${NODE_DIR_BASE}" ]; then + rmdir "${NODE_DIR_BASE}" fi - if [ -d ${SHARED_DIR} ]; then - rm -rf ${SHARED_DIR} + if [ -d "${SHARED_DIR} "]; then + rm -rf "${SHARED_DIR}" fi fi + # TODO(kmacleod): do we need this? # Mark the DNF cache expired dnf clean expire-cache } @@ -378,48 +549,33 @@ function handle_delete { function get_patches_from_host { local host_patch_repo=/var/www/pages/updates/rel-${ISO_VERSION} - if [ ! -d ${host_patch_repo} ]; then + if [ ! -d "${host_patch_repo}" ]; then log_error "Patch repo not found: ${host_patch_repo}" # Don't fail, as there could be scenarios where there's nothing on # the host related to the release on the ISO return fi - mkdir -p ${SHARED_DIR}/patches - if [ $? -ne 0 ]; then - log_error "Failed to create directory: ${SHARED_DIR}/patches" - exit 1 - fi + mkdir -p "${SHARED_DIR}/patches" + check_rc_exit $? "Failed to create directory: ${SHARED_DIR}/patches" - rsync -a ${host_patch_repo}/repodata ${SHARED_DIR}/patches/ - if [ $? -ne 0 ]; then - log_error "Failed to copy ${host_patch_repo}/repodata" - exit 1 - fi + rsync -a "${host_patch_repo}/repodata" "${SHARED_DIR}/patches/" + check_rc_exit $? "Failed to copy ${host_patch_repo}/repodata" - if [ -d ${host_patch_repo}/Packages ]; then - rsync -a ${host_patch_repo}/Packages ${SHARED_DIR}/patches/ - if [ $? -ne 0 ]; then - log_error "Failed to copy ${host_patch_repo}/Packages" - exit 1 - fi - elif [ ! -d ${SHARED_DIR}/patches/Packages ]; then + if [ -d "${host_patch_repo}/Packages" ]; then + rsync -a "${host_patch_repo}/Packages" "${SHARED_DIR}/patches/" + check_rc_exit $? "Failed to copy ${host_patch_repo}/Packages" + elif [ ! -d "${SHARED_DIR}/patches/Packages" ]; then # Create an empty Packages dir - mkdir ${SHARED_DIR}/patches/Packages - if [ $? -ne 0 ]; then - log_error "Failed to create ${SHARED_DIR}/patches/Packages" - exit 1 - fi + mkdir "${SHARED_DIR}/patches/Packages" + check_rc_exit $? "Failed to create ${SHARED_DIR}/patches/Packages" fi mkdir -p \ - ${SHARED_DIR}/patches/metadata/available \ - ${SHARED_DIR}/patches/metadata/applied \ - ${SHARED_DIR}/patches/metadata/committed - if [ $? -ne 0 ]; then - log_error "Failed to create directory: ${SHARED_DIR}/patches/metadata/${state}" - exit 1 - fi + "${SHARED_DIR}/patches/metadata/available" \ + "${SHARED_DIR}/patches/metadata/applied" \ + "${SHARED_DIR}/patches/metadata/committed" + check_rc_exit $? "Failed to create director(ies): ${SHARED_DIR}/patches/metadata/..." local metadata_to_copy= for state in applied committed; do @@ -429,11 +585,8 @@ function get_patches_from_host { metadata_to_copy=$(find /opt/patching/metadata/${state} -type f -exec grep -q "<sw_version>${ISO_VERSION}</sw_version>" {} \; -print) if [ -n "${metadata_to_copy}" ]; then - rsync -a ${metadata_to_copy} ${SHARED_DIR}/patches/metadata/${state}/ - if [ $? -ne 0 ]; then - log_error "Failed to copy ${state} patch metadata" - exit 1 - fi + rsync -a "${metadata_to_copy}" "${SHARED_DIR}/patches/metadata/${state}/" + check_rc_exit $? "Failed to copy ${state} patch metadata" fi done } @@ -459,49 +612,34 @@ function extract_pkg_to_workdir { return fi - if [ ! -f "${pkgfile}" ]; then - log_error "File doesn't exist, unable to extract: ${pkgfile}" - exit 1 - fi + [ -f "${pkgfile}" ] || fatal_error "File doesn't exist, unable to extract: ${pkgfile}" - pushd ${WORKDIR} >/dev/null - echo "Extracting files from ${pkgfile}" + pushd "${WORKDIR}" >/dev/null + log_info "Extracting files from ${pkgfile}" rpm2cpio ${pkgfile} | cpio -idmv - if [ $? -ne 0 ]; then - log_error "Failed to extract files from ${pkgfile}" - exit 1 - fi + check_rc_exit $? "Failed to extract files from ${pkgfile}" popd >/dev/null } function extract_shared_files { - if [ -d ${SHARED_DIR} ]; then + if [ -d "${SHARED_DIR}" ]; then # If the shared dir already exists, assume we don't need to re-extract return fi - mkdir -p ${SHARED_DIR} - if [ $? -ne 0 ]; then - log_error "Failed to create directory: ${SHARED_DIR}" - exit 1 - fi + mkdir -p "${SHARED_DIR}" + check_rc_exit $? "Failed to create directory: ${SHARED_DIR}" # Check ISO content - if [ ! -f ${MNTDIR}/LiveOS/squashfs.img ]; then - log_error "squashfs.img not found on ${INPUT_ISO}" - exit 1 - fi + [ -f "${MNTDIR}/LiveOS/squashfs.img" ] || fatal_error "squashfs.img not found on ${INPUT_ISO}" # Setup shared patch data - if [ ${PATCHES_FROM_HOST} = "yes" ]; then + if [ "${PATCHES_FROM_HOST}" = "yes" ]; then get_patches_from_host else - if [ -d ${MNTDIR}/patches ]; then - rsync -a ${MNTDIR}/patches/ ${SHARED_DIR}/patches/ - if [ $? -ne 0 ]; then - log_error "Failed to copy patches repo from ${INPUT_ISO}" - exit 1 - fi + if [ -d "${MNTDIR}/patches" ]; then + rsync -a "${MNTDIR}/patches/" "${SHARED_DIR}/patches/" + check_rc_exit $? "Failed to copy patches repo from ${INPUT_ISO}" fi fi @@ -509,63 +647,53 @@ function extract_shared_files { dnf clean expire-cache local squashfs_img_file=${MNTDIR}/LiveOS/squashfs.img - if [ ${PATCHES_FROM_HOST} = "yes" ]; then + if [ "${PATCHES_FROM_HOST}" = "yes" ]; then extract_pkg_to_workdir 'pxe-network-installer' local patched_squashfs_img_file=${WORKDIR}/var/www/pages/feed/rel-${ISO_VERSION}/LiveOS/squashfs.img - if [ -f ${patched_squashfs_img_file} ]; then + if [ -f "${patched_squashfs_img_file}" ]; then # Use the patched squashfs.img squashfs_img_file=${patched_squashfs_img_file} fi fi - mkdir ${SHARED_DIR}/LiveOS - rsync -a ${squashfs_img_file} ${SHARED_DIR}/LiveOS/ - if [ $? -ne 0 ]; then - log_error "Failed to copy rootfs: ${patched_squashfs_img_file}" - exit 1 + mkdir "${SHARED_DIR}/LiveOS" + rsync -a "${squashfs_img_file}" "${SHARED_DIR}/LiveOS/" + check_rc_exit $? "Failed to copy rootfs: ${patched_squashfs_img_file}" + + # The CentOS kickstart files are on the system controller in their own directory. + # Copy them into miniboot ISO. + [ -f /etc/build.info ] || fatal_error "File /etc/build.info does not exist. Cannot determine software version." + source /etc/build.info + [ -n "$SW_VERSION" ] || fatal_error "SW_VERSION is not in /etc/build.info. Cannot determine software version." + local kickstart_files_dir=/var/www/pages/feed/rel-${SW_VERSION}/kickstart/centos + + mkdir "${SHARED_DIR}/kickstart" || fatal_error "mkdir ${SHARED_DIR}/kickstart failed" + rsync -a "${kickstart_files_dir}"/miniboot_*.cfg "${SHARED_DIR}"/kickstart + check_rc_exit $? "Failed to copy kickstart files from ${kickstart_files_dir}" + + # Any files in $KICKSTART_OVERRIDE_DIR are used in place of the files from above: + if [ -d "${KICKSTART_OVERRIDE_DIR}" ] && \ + [ "$(echo "${KICKSTART_OVERRIDE_DIR}"/miniboot_*.cfg)" != \ + "${KICKSTART_OVERRIDE_DIR}/miniboot_*.cfg" ]; then + log_info "Copying override cfg files from ${KICKSTART_OVERRIDE_DIR}" + cp "${KICKSTART_OVERRIDE_DIR}"/miniboot_*.cfg "${SHARED_DIR}"/kickstart + check_rc_exit $? "Failed to copy override kickstart files from ${KICKSTART_OVERRIDE_DIR}" fi - local kickstart_files_dir=${MNTDIR}/ - if [ ${PATCHES_FROM_HOST} = "yes" ]; then - extract_pkg_to_workdir 'platform-kickstarts' + rsync -a "${MNTDIR}/isolinux.cfg" "${SHARED_DIR}/" + check_rc_exit $? "Failed to copy isolinux.cfg from ${INPUT_ISO}" - local patched_kickstart_files_dir=${WORKDIR}/var/www/pages/feed/rel-${ISO_VERSION} - if [ -f ${patched_kickstart_files_dir}/miniboot_controller_ks.cfg ]; then - # Use the patched kickstart files - kickstart_files_dir=${patched_kickstart_files_dir} - fi - fi + rsync -a "${MNTDIR}/Packages/" "${SHARED_DIR}/Packages/" + check_rc_exit $? "Failed to copy base packages from ${INPUT_ISO}" - mkdir ${SHARED_DIR}/kickstart/ - rsync -a ${kickstart_files_dir}/miniboot_*.cfg ${SHARED_DIR}/kickstart - if [ $? -ne 0 ]; then - log_error "Failed to copy kickstart files from ${kickstart_files_dir}" - exit 1 - fi - - rsync -a ${MNTDIR}/isolinux.cfg ${SHARED_DIR}/ - if [ $? -ne 0 ]; then - log_error "Failed to copy isolinux.cfg from ${INPUT_ISO}" - exit 1 - fi - - rsync -a ${MNTDIR}/Packages/ ${SHARED_DIR}/Packages/ - if [ $? -ne 0 ]; then - log_error "Failed to copy base packages from ${INPUT_ISO}" - exit 1 - fi - - rsync -a ${MNTDIR}/repodata/ ${SHARED_DIR}/repodata/ - if [ $? -ne 0 ]; then - log_error "Failed to copy base repodata from ${INPUT_ISO}" - exit 1 - fi + rsync -a "${MNTDIR}/repodata/" "${SHARED_DIR}/repodata/" + check_rc_exit $? "Failed to copy base repodata from ${INPUT_ISO}" } function extract_node_files { # Copy files for mini ISO build - rsync -a \ + rsync ${VERBOSE_RSYNC} -a \ --exclude LiveOS/ \ --exclude Packages/ \ --exclude repodata/ \ @@ -576,44 +704,34 @@ function extract_node_files { --exclude '*_ks.cfg' \ --exclude ks.cfg \ --exclude ks \ - ${MNTDIR}/ ${BUILDDIR}/ - rc=$? - if [ $rc -ne 0 ]; then - log_error "Call to rsync ISO content. Aborting..." - exit $rc - fi + "${MNTDIR}/" "${BUILDDIR}/" + check_rc_exit $? "Failed to rsync ISO content from ${MNTDIR} to ${BUILDDIR}" - if [ ${PATCHES_FROM_HOST} = "yes" ]; then + if [ "${PATCHES_FROM_HOST}" = "yes" ]; then local patched_initrd_file=${WORKDIR}/pxeboot/rel-${ISO_VERSION}/installer-intel-x86-64-initrd_1.0 local patched_vmlinuz_file=${WORKDIR}/pxeboot/rel-${ISO_VERSION}/installer-bzImage_1.0 # First, check to see if pxe-network-installer is already extracted. # If this is the first setup for this ISO, it will have been extracted # during the shared setup, and we don't need to do it again. - if [ ! -f ${patched_initrd_file} ]; then + if [ ! -f "${patched_initrd_file}" ]; then extract_pkg_to_workdir 'pxe-network-installer' fi # Copy patched files, as appropriate - if [ -f ${patched_initrd_file} ]; then - rsync -a ${patched_initrd_file} ${BUILDDIR}/initrd.img - if [ $? -ne 0 ]; then - log_error "Failed to copy ${patched_initrd_file}" - exit 1 - fi + if [ -f "${patched_initrd_file}" ]; then + rsync -a "${patched_initrd_file}" "${BUILDDIR}"/initrd.img + check_rc_exit $? "Failed to copy ${patched_initrd_file}" fi - if [ -f ${patched_vmlinuz_file} ]; then - rsync -a ${patched_vmlinuz_file} ${BUILDDIR}/vmlinuz - if [ $? -ne 0 ]; then - log_error "Failed to copy ${patched_vmlinuz_file}" - exit 1 - fi + if [ -f "${patched_vmlinuz_file}" ]; then + rsync -a "${patched_vmlinuz_file}" "${BUILDDIR}"/vmlinuz + check_rc_exit $? "Failed to copy ${patched_vmlinuz_file}" fi fi # Setup syslinux and grub cfg files - generate_boot_cfg ${BUILDDIR} + generate_boot_cfg "${BUILDDIR}" # Set/update boot parameters if [ ${#PARAMS[@]} -gt 0 ]; then @@ -621,17 +739,13 @@ function extract_node_files { param=${p%%=*} # Strip from the first '=' on value=${p#*=} # Strip to the first '=' - update_parameter ${BUILDDIR} "${param}" "${value}" + update_parameter "${BUILDDIR}" "${param}" "${value}" done fi unmount_efiboot_img - mkdir -p ${NODE_DIR} - if [ $? -ne 0 ]; then - log_error "Failed to create ${NODE_DIR}" - exit 1 - fi + mkdir -p "${NODE_DIR}" || fatal_error "Failed to create ${NODE_DIR}" # Setup symlinks to the shared content, which lighttpd can serve pushd ${NODE_DIR} >/dev/null @@ -640,7 +754,8 @@ function extract_node_files { # Rebuild the ISO OUTPUT_ISO=${NODE_DIR}/bootimage.iso - mkisofs -o ${OUTPUT_ISO} \ + log_info "Creating ${OUTPUT_ISO} from BUILDDIR: ${BUILDDIR}" + mkisofs -o "${OUTPUT_ISO}" \ -R -D -A 'oe_iso_boot' -V 'oe_iso_boot' \ -quiet \ -b isolinux.bin -c boot.cat -no-emul-boot \ @@ -648,19 +763,19 @@ function extract_node_files { -eltorito-alt-boot \ -e images/efiboot.img \ -no-emul-boot \ - ${BUILDDIR} - - isohybrid --uefi ${OUTPUT_ISO} - implantisomd5 ${OUTPUT_ISO} + "${BUILDDIR}" + check_rc_exit $? "mkisofs failed" + isohybrid --uefi "${OUTPUT_ISO}" + check_rc_exit $? "isohybrid failed" + implantisomd5 "${OUTPUT_ISO}" + check_rc_exit $? "implantisomd5 failed" + log_path_size "$OUTPUT_ISO" "Size of bootimage.iso" # Setup the kickstart local ksfile=${SHARED_DIR}/kickstart/miniboot_${KS_NODETYPE}_ks.cfg - cp ${ksfile} ${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg - if [ $? -ne 0 ]; then - log_error "Failed to copy ${ksfile} to ${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg" - exit 1 - fi + cp "${ksfile}" "${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg" + check_rc_exit $? "Failed to copy ${ksfile} to ${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg" # Number of dirs in the NODE_URL: Count the / characters, subtracting 2 for http:// or https:// DIRS=$(($(grep -o "/" <<< "$NODE_URL" | wc -l) - 2)) @@ -671,118 +786,56 @@ function extract_node_files { sed -i "s#xxxHTTP_URLxxx#${NODE_URL_SED}#g; s#xxxHTTP_URL_PATCHESxxx#${NODE_URL_SED}/patches#g; s#NUM_DIRS#${DIRS}#g" \ - ${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg + "${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg" # Append the custom addon if [ -n "${ADDON}" ]; then - cat <<EOF >>${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg + cat <<EOF >> "${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg" %post --erroronfail # Source common functions . /tmp/ks-functions.sh -$(cat ${ADDON}) +$(cat "${ADDON}") %end EOF fi } +function create_miniboot_iso { + # Determine release version from ISO + [ -f ${MNTDIR}/upgrades/version ] || fatal_error "Version info not found on input ISO: ${INPUT_ISO}" + ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION}) + if [ -z "${ISO_VERSION}" ]; then + fatal_error "Failed to determine version of installation ISO from ${MNTDIR}/upgrades/version" + fi + + # Copy the common files from the ISO, if needed + extract_shared_files + + # Extract/generate the node-specific files + extract_node_files +} + # # Main # - -# Check script dependencies -check_requirements - -# Validate parameters - -# Check mandatory parameters - -check_required_param "--id" "${NODE_ID}" -check_required_param "--www-root" "${WWW_ROOT_DIR}" - -declare NODE_DIR_BASE="${WWW_ROOT_DIR}/nodes" -declare NODE_DIR="${NODE_DIR_BASE}/${NODE_ID}" -declare SHARED_DIR="${WWW_ROOT_DIR}/shared" - -if [ ! -d "${WWW_ROOT_DIR}" ]; then - log_error "Root directory ${WWW_ROOT_DIR} does not exist" - exit 1 -fi - -# Grab the lock, to protect against simultaneous execution -LOCK_FILE=/var/run/.gen-bootloader-iso.lock -exec 200>${LOCK_FILE} -flock -w ${LOCK_TMOUT} 200 -if [ $? -ne 0 ]; then - log_error "Failed waiting for lock: ${LOCK_FILE}" - exit 1 -fi - -# Check for deletion -if [ ${DELETE} = "yes" ]; then - handle_delete +function main { + # if [ "$(get_os)" != centos ]; then + # fatal_error "This script must be invoked on CentOS only" + # fi + parse_arguments "$@" + initialize_and_lock + mount_iso "${INPUT_ISO}" "${SCRATCH_DIR}" + create_miniboot_iso + unmount_iso exit 0 +} + +# Execute main if script is executed directly (not sourced): +# This allows for shunit2 testing +if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then + main "$@" fi - -# Handle extraction and setup - -check_required_param "--input" "${INPUT_ISO}" -check_required_param "--default-boot" "${DEFAULT_GRUB_ENTRY}" -check_required_param "--base-url" "${BASE_URL}" -check_required_param "--boot-ip" "${BOOT_IP}" -check_required_param "--boot-interface" "${BOOT_INTERFACE}" - -declare NODE_URL="${BASE_URL%\/}/nodes/${NODE_ID}" - -if [ ! -f ${INPUT_ISO} ]; then - log_error "Input file does not exist: ${INPUT_ISO}" - exit 1 -fi - -if [ -d ${NODE_DIR} ]; then - log_error "Output dir already exists: ${NODE_DIR}" - exit 1 -fi - -# Run cleanup on any exit -trap cleanup EXIT - -BUILDDIR=$(mktemp -d -p /scratch gen_bootloader_build_XXXXXX) -if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then - log_error "Failed to create builddir. Aborting..." - exit 1 -fi - -WORKDIR=$(mktemp -d -p /scratch gen_bootloader_workdir_XXXXXX) -if [ -z "${WORKDIR}" -o ! -d ${WORKDIR} ]; then - log_error "Failed to create builddir. Aborting..." - exit 1 -fi - -mount_iso ${INPUT_ISO} /scratch - -# Determine release version from ISO -if [ ! -f ${MNTDIR}/upgrades/version ]; then - log_error "Version info not found on ${INPUT_ISO}" - exit 1 -fi - -ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION}) -if [ -z "${ISO_VERSION}" ]; then - log_error "Failed to determine version of installation ISO" - exit 1 -fi - -# Copy the common files from the ISO, if needed -extract_shared_files - -# Extract/generate the node-specific files -extract_node_files - -unmount_iso - -exit 0 - diff --git a/utilities/platform-util/scripts/gen-bootloader-iso.sh b/utilities/platform-util/scripts/gen-bootloader-iso.sh index affdccc4..0bbbdb69 100755 --- a/utilities/platform-util/scripts/gen-bootloader-iso.sh +++ b/utilities/platform-util/scripts/gen-bootloader-iso.sh @@ -29,18 +29,17 @@ BOOT_HOSTNAME= BOOT_INTERFACE= BOOT_IP= BOOT_NETMASK= -MINIBOOT_INITRD_FILE=/var/miniboot/initrd-mini # populated by the loadbuild at this location DEFAULT_GRUB_ENTRY= DEFAULT_SYSLINUX_ENTRY= DELETE="no" GRUB_TIMEOUT=-1 INITRD_FILE= -INSTALL_TYPE= -REPACK=no # REPACK initrd is disabled until signing is fixed INPUT_ISO= +INSTALL_TYPE= LOCK_FILE=/var/run/.gen-bootloader-iso.lock LOCK_TMOUT=600 # Wait up to 10 minutes, by default LOG_TAG=$SCRIPTNAME +MINIBOOT_INITRD_FILE=/var/miniboot/initrd-mini # populated by the loadbuild at this location NODE_ID= OUTPUT_ISO= REPACK=yes # Repack/trim the initrd and kernel images by default @@ -49,7 +48,6 @@ TIMEOUT=100 VERBOSE=${VERBOSE:-} VERBOSE_LOG_DIR=/var/log/dcmanager/miniboot VERBOSE_OVERRIDE_FILE=/tmp/gen-bootloader-verbose # turn on verbose if this file is present -WORKDIR= WWW_ROOT_DIR= XZ_ARGS="--threads=0 -9 --format=lzma" @@ -86,17 +84,6 @@ function log_warn { logger -i -s -t "${LOG_TAG}" -- "WARN: $*" } -function get_path_size { - local path=$1 - du -hs "$path" | awk '{print $1}' -} - -function log_path_size { - local path=$1 - local msg=$2 - log_info "$msg: $(get_path_size "$path")" -} - function fatal_error { logger -i -s -t "${LOG_TAG}" -- "FATAL: $*" exit 1 @@ -127,6 +114,17 @@ function get_os { esac } +function get_path_size { + local path=$1 + du -hs "$path" | awk '{print $1}' +} + +function log_path_size { + local path=$1 + local msg=$2 + log_info "$msg: $(get_path_size "$path")" +} + function usage { cat <<ENDUSAGE Description: Sets up a mini bootimage.iso that includes the minimum required to @@ -224,7 +222,6 @@ function parse_arguments { INPUT_ISO=$2 shift 2 ;; - # TODO: do we need to support --addon for debian? --addon) ADDON=$2 shift 2 @@ -307,12 +304,12 @@ function parse_arguments { esac ;; --timeout) - timeout_arg=$2 + local -i timeout_arg=$2 shift 2 - if [ $(( timeout_arg )) -gt 0 ]; then + if [ ${timeout_arg} -gt 0 ]; then TIMEOUT=$(( timeout_arg * 10 )) GRUB_TIMEOUT=${timeout_arg} - elif [ $(( timeout_arg )) -eq 0 ]; then + elif [ ${timeout_arg} -eq 0 ]; then GRUB_TIMEOUT=0.001 fi ;; @@ -341,9 +338,9 @@ function parse_arguments { shift 2 ;; --lock-timeout) - LOCK_TMOUT=$2 + local -i LOCK_TMOUT=$2 shift 2 - if [ "$LOCK_TMOUT" -le 0 ]; then + if [ "${LOCK_TMOUT}" -le 0 ]; then echo "Lock timeout must be greater than 0" >&2 exit 1 fi @@ -387,22 +384,25 @@ function initialize_and_lock { check_required_param "--id" "${NODE_ID}" check_required_param "--www-root" "${WWW_ROOT_DIR}" [ -d "${WWW_ROOT_DIR}" ] || fatal_error "Root directory ${WWW_ROOT_DIR} does not exist" - [ -d "${WWW_ROOT_DIR}/iso" ] || mkdir "${WWW_ROOT_DIR}/iso" - [ -f "$VERBOSE_OVERRIDE_FILE" ] && VERBOSE=1 - if [ -n "$VERBOSE" ]; then + [ -f "${VERBOSE_OVERRIDE_FILE}" ] && VERBOSE=1 + if [ -n "${VERBOSE}" ]; then VERBOSE_RSYNC="--verbose" XZ_ARGS="--verbose $XZ_ARGS" # log all output to file - if [ ! -d "$(dirname "$VERBOSE_LOG_DIR")" ]; then + if [ ! -d "$(dirname "${VERBOSE_LOG_DIR}")" ]; then # For testing: the base directory does not exist - use /tmp instead VERBOSE_LOG_DIR=/tmp/miniboot fi - [ -d "$VERBOSE_LOG_DIR" ] || mkdir -p "$VERBOSE_LOG_DIR" + [ -d "${VERBOSE_LOG_DIR}" ] || mkdir -p "${VERBOSE_LOG_DIR}" local logfile="${VERBOSE_LOG_DIR}/gen-bootloader-iso-${NODE_ID}.log" - echo "Verbose: logging output to $logfile" - exec > >(tee "$logfile") 2>&1 + [ -f "${logfile}" ] && rm -f "${logfile}" + touch "${logfile}" + echo "Verbose: logging output to ${logfile}" + echo "$(date) Starting $0" + printenv >> "${logfile}" + exec > >(tee --append "${logfile}") 2>&1 fi # Initialize dynamic variables @@ -439,14 +439,14 @@ function initialize_and_lock { # Run cleanup on any exit trap cleanup_on_exit EXIT - BUILDDIR=$(mktemp -d -p "$SCRATCH_DIR" gen_bootloader_build_XXXXXX) + BUILDDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_build_XXXXXX) if [ -z "${BUILDDIR}" ] || [ ! -d "${BUILDDIR}" ]; then - fatal_error "Failed to create builddir: $BUILDDIR" + fatal_error "Failed to create builddir: ${BUILDDIR}" fi - WORKDIR=$(mktemp -d -p "$SCRATCH_DIR" gen_bootloader_initrd_XXXXXX) + WORKDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_initrd_XXXXXX) if [ -z "${WORKDIR}" ] || [ ! -d "${WORKDIR}" ]; then - fatal_error "Failed to create initrd extract directory: $WORKDIR" + fatal_error "Failed to create WORKDIR directory: $WORKDIR" fi } @@ -468,12 +468,17 @@ function generate_boot_cfg { for p in "${PARAMS[@]}"; do param=${p%%=*} value=${p#*=} - # Pull the boot device out of PARAMS and convert to instdev - if [ "$param" = "boot_device" ]; then - log_info "Setting instdev=$value from boot_device param" - instdev=$value + if [ "${param}" = "${p}" ]; then + # there is no '=' in the parameter; include it directly: + PARAM_LIST="${PARAM_LIST} ${param}" + else + # Pull the boot device out of PARAMS and convert to instdev + if [ "$param" = "boot_device" ]; then + log_info "Setting instdev=$value from boot_device param" + instdev=$value + fi + PARAM_LIST="${PARAM_LIST} ${param}=${value}" fi - PARAM_LIST="${PARAM_LIST} ${param}=${value}" done fi log_verbose "Parameters: ${PARAM_LIST}" @@ -557,8 +562,8 @@ LABEL 1 EOF done - log_verbose "Generating grub.cfg, install_type: $INSTALL_TYPE, default: $DEFAULT_GRUB_ENTRY, timeout: $GRUB_TIMEOUT" - for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do + log_verbose "Generating grub.cfg, install_type: ${INSTALL_TYPE}, default: ${DEFAULT_GRUB_ENTRY}, timeout: ${GRUB_TIMEOUT}" + for f in "${isodir}/EFI/BOOT/grub.cfg" "${EFI_MOUNT}/EFI/BOOT/grub.cfg"; do cat <<EOF > "${f}" default=${DEFAULT_GRUB_ENTRY} timeout=${GRUB_TIMEOUT} @@ -581,13 +586,13 @@ menuentry 'UEFI Graphical Console' --id=graphical { } EOF done -if [ -n "$VERBOSE" ]; then - log_verbose "Contents of ${isodir}/EFI/BOOT/grub.cfg" - cat "${isodir}/EFI/BOOT/grub.cfg" - log_verbose "" - log_verbose "Contents of ${EFI_MOUNT}/EFI/BOOT/grub.cfg" - cat "${EFI_MOUNT}/EFI/BOOT/grub.cfg" -fi + if [ -n "$VERBOSE" ]; then + log_verbose "Contents of ${isodir}/EFI/BOOT/grub.cfg" + cat "${isodir}/EFI/BOOT/grub.cfg" + log_verbose "" + log_verbose "Contents of ${EFI_MOUNT}/EFI/BOOT/grub.cfg" + cat "${EFI_MOUNT}/EFI/BOOT/grub.cfg" + fi } function cleanup_on_exit { @@ -624,28 +629,28 @@ function handle_delete { } function create_miniboot_iso { - log_info "Creating minitboot ISO" + log_info "Creating miniboot ISO" # Copy files for mini ISO build - rsync $VERBOSE_RSYNC -a \ + rsync ${VERBOSE_RSYNC} -a \ --exclude ostree_repo \ --exclude pxeboot \ "${MNTDIR}/" "${BUILDDIR}" check_rc_exit $? "Failed to rsync ISO from $MNTDIR to $BUILDDIR" - if [ "$REPACK" = yes ]; then + if [ "${REPACK}" = yes ]; then # Use default initrd-mini location if none specified # This picks up the initrd-mini file if it is available # (included in ISO by the loadbuild). Otherwise we warn # and continue without repacking initrd - instead using # the original from the ISO. - if [ -z "$INITRD_FILE" ]; then - INITRD_FILE="$MINIBOOT_INITRD_FILE" + if [ -z "${INITRD_FILE}" ]; then + INITRD_FILE="${MINIBOOT_INITRD_FILE}" fi - if [ -f "$INITRD_FILE" ]; then + if [ -f "${INITRD_FILE}" ]; then if [ -f "${INITRD_FILE}.sig" ]; then # Overwrite the original ISO initrd file: log_info "Repacking miniboot ISO using initrd: ${INITRD_FILE} and ${INITRD_FILE}.sig" - cp "$INITRD_FILE" "${BUILDDIR}/initrd" + cp "${INITRD_FILE}" "${BUILDDIR}/initrd" check_rc_exit $? "copy initrd failed" cp "${INITRD_FILE}.sig" "${BUILDDIR}/initrd.sig" check_rc_exit $? "copy initrd.sig failed" @@ -653,10 +658,10 @@ function create_miniboot_iso { log_error "No initrd.sig found at: ${INITRD_FILE}.sig ...skipping initrd repack" fi else - log_warn "Could not find initrd file at $INITRD_FILE ...skipping initrd repack" + log_warn "Could not find initrd file at ${INITRD_FILE} ...skipping initrd repack" fi log_info "Trimming miniboot ISO content" - log_path_size "$BUILDDIR" "Size of extracted miniboot before trim" + log_path_size "${BUILDDIR}" "Size of extracted miniboot before trim" # Remove unused kernel images: rm "${BUILDDIR}"/{bzImage,bzImage-rt} check_rc_exit $? "failed to trim miniboot iso files" @@ -668,18 +673,18 @@ function create_miniboot_iso { # where any .cfg files are now copied into the /kickstart directory in the ISO # Any files in this override directory can replace the files from the ISO copied # from the rsync above. - if [ -n "$KICKSTART_OVERRIDE_DIR" ] \ - && [ -d "$KICKSTART_OVERRIDE_DIR" ] \ - && [ "$(echo "$KICKSTART_OVERRIDE_DIR/"*.cfg)" != "$KICKSTART_OVERRIDE_DIR/*.cfg" ]; then - log_info "Copying .cfg files from KICKSTART_OVERRIDE_DIR=$KICKSTART_OVERRIDE_DIR to $BUILDDIR/kickstart" - cp "$KICKSTART_OVERRIDE_DIR/"*.cfg "$BUILDDIR/kickstart" + if [ -n "${KICKSTART_OVERRIDE_DIR}" ] \ + && [ -d "${KICKSTART_OVERRIDE_DIR}" ] \ + && [ "$(echo "${KICKSTART_OVERRIDE_DIR}/"*.cfg)" != "${KICKSTART_OVERRIDE_DIR}/*.cfg" ]; then + log_info "Copying .cfg files from KICKSTART_OVERRIDE_DIR=${KICKSTART_OVERRIDE_DIR} to ${BUILDDIR}/kickstart" + cp "${KICKSTART_OVERRIDE_DIR}/"*.cfg "${BUILDDIR}/kickstart" fi # Setup syslinux and grub cfg files if [ -z "${EFI_MOUNT}" ]; then mount_efiboot_img "${BUILDDIR}" check_rc_exit $? "failed to mount EFI" - log_info "Using EFI_MOUNT=$EFI_MOUNT" + log_info "Using EFI_MOUNT=${EFI_MOUNT}" fi generate_boot_cfg "${BUILDDIR}" unmount_efiboot_img @@ -688,7 +693,7 @@ function create_miniboot_iso { # Rebuild the ISO OUTPUT_ISO=${NODE_DIR}/bootimage.iso - log_info "Creating $OUTPUT_ISO from BUILDDIR: ${BUILDDIR}" + log_info "Creating ${OUTPUT_ISO} from BUILDDIR: ${BUILDDIR}" mkisofs -o "${OUTPUT_ISO}" \ -A 'instboot' -V 'instboot' \ -quiet -U -J -joliet-long -r -iso-level 2 \ @@ -718,7 +723,7 @@ function main { fi parse_arguments "$@" initialize_and_lock - mount_iso "$INPUT_ISO" "$SCRATCH_DIR" + mount_iso "${INPUT_ISO}" "${SCRATCH_DIR}" create_miniboot_iso unmount_iso exit 0 diff --git a/utilities/platform-util/scripts/gen-prestaged-iso-centos.sh b/utilities/platform-util/scripts/gen-prestaged-iso-centos.sh index 23d61011..431d44c7 100755 --- a/utilities/platform-util/scripts/gen-prestaged-iso-centos.sh +++ b/utilities/platform-util/scripts/gen-prestaged-iso-centos.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2021-2022 Wind River Systems, Inc. +# Copyright (c) 2021-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -14,26 +14,30 @@ # exceed 4 GB. Multiple archives can be provided. All archives # must have the suffix 'tar.gz'. -function log_error { - echo "$@" >&2 +# shellcheck disable=1091 # don't warn about following 'source <file>' +# shellcheck disable=2164 # don't warn about pushd/popd failures +# shellcheck disable=2181 # don't warn about using rc=$? + +function log_fatal { + echo "$(date "+%F %H:%M:%S") FATAL: $*" >&2 + exit 1 } -# Source shared utility functions -DIR_NAME=$(dirname $0) -if [ ! -f ${DIR_NAME}/stx-iso-utils-centos.sh ]; then - log_error "Unable to find required utility: stx-iso-utils-centos.sh" - exit 1 -fi +function log_error { + echo "$(date "+%F %H:%M:%S"): ERROR: $*" >&2 +} -source $(dirname $0)/stx-iso-utils-centos.sh +function log_info { + echo "$(date "+%F %H:%M:%S"): INFO: $*" >&2 +} +# Usage manual. function usage { cat <<ENDUSAGE -Utility to convert a StarlingX installation iso into a prestaged subcloud -installation iso. +Utility to convert a StarlingX installation iso into a CentOS prestaged subcloud installation iso. Usage: - $(basename $0) --input <input bootimage.iso> + $(basename "$0") --input <input bootimage.iso> --output <output bootimage.iso> [ --images <images.tar.gz> ] [ --patch <patch-name.patch> ] @@ -83,6 +87,7 @@ ENDUSAGE } function cleanup { + # This is invoked from the trap handler. common_cleanup } @@ -94,31 +99,31 @@ function set_default_label { local isodir=$1 if [ -z "${EFI_MOUNT}" ]; then - mount_efiboot_img ${isodir} + mount_efiboot_img "${isodir}" fi - for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do + for f in "${isodir}/isolinux.cfg" "${isodir}/syslinux.cfg"; do if [ "${DEFAULT_LABEL}" = "NULL" ]; then # Remove default, if set - grep -q '^default' ${f} + grep -q '^default' "${f}" if [ $? -eq 0 ]; then - sed -i '/^default/d' ${f} + sed -i '/^default/d' "${f}" fi else - grep -q '^default' ${f} + grep -q '^default' "${f}" if [ $? -ne 0 ]; then - cat <<EOF >> ${f} + cat <<EOF >> "${f}" default ${DEFAULT_SYSLINUX_ENTRY} EOF else - sed -i "s/^default.*/default ${DEFAULT_SYSLINUX_ENTRY}/" ${f} + sed -i "s/^default.*/default ${DEFAULT_SYSLINUX_ENTRY}/" "${f}" fi fi done for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do - sed -i "s/^default=.*/default=\"${DEFAULT_GRUB_ENTRY}\"/" ${f} + sed -i "s/^default=.*/default=\"${DEFAULT_GRUB_ENTRY}\"/" "${f}" done } @@ -126,27 +131,27 @@ function set_timeout { local isodir=$1 if [ -z "${EFI_MOUNT}" ]; then - mount_efiboot_img ${isodir} + mount_efiboot_img "${isodir}" fi for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do - sed -i "s/^timeout.*/timeout ${TIMEOUT}/" ${f} + sed -i "s/^timeout.*/timeout ${TIMEOUT}/" "${f}" done for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do - sed -i "s/^timeout=.*/timeout=${GRUB_TIMEOUT}/" ${f} + sed -i "s/^timeout=.*/timeout=${GRUB_TIMEOUT}/" "${f}" - grep -q "^ set timeout=" ${f} + grep -q "^ set timeout=" "${f}" if [ $? -eq 0 ]; then # Submenu timeout is already added. Update the value - sed -i -e "s#^ set timeout=.*# set timeout=${GRUB_TIMEOUT}#" ${f} + sed -i -e "s#^ set timeout=.*# set timeout=${GRUB_TIMEOUT}#" "${f}" if [ $? -ne 0 ]; then log_error "Failed to update grub timeout" exit 1 fi else # Parameter doesn't exist. Add it to the cmdline - sed -i -e "/^submenu/a \ \ set timeout=${GRUB_TIMEOUT}" ${f} + sed -i -e "/^submenu/a \ \ set timeout=${GRUB_TIMEOUT}" "${f}" if [ $? -ne 0 ]; then log_error "Failed to add grub timeout" exit 1 @@ -171,13 +176,14 @@ function normalized_path { local path="${1}" local default_fn="${2}" - local path_name="$(basename "${path}")" - local path_dir="$(dirname "${path}")" + local path_name path_dir + path_name="$(basename "${path}")" + path_dir="$(dirname "${path}")" # If 'path' ends in '/' then path was intended to be a directory - if [ "${path:(-1):1}" == "/" ]; then + if [ "${path: -1:1}" = "/" ]; then # Note: space is required after : to distinguish from ${path:-...} # Drop the trailing '/' - path_dir="${path:0:(-1)}" + path_dir="${path:0:-1}" path_name="${default_fn}" fi @@ -198,7 +204,6 @@ function normalized_path { fi } - function copy_to_iso { local src="${1}" local dest="${2}" @@ -208,9 +213,7 @@ function copy_to_iso { local default_dest= local final_dest= local final_dest_dir= - local default_md5= local final_md5= - local final_md5_dir= if [ -z "${src}" ] || [ -z "${dest}" ]; then log_error "Error: copy_to_iso: missing argument" @@ -227,9 +230,7 @@ function copy_to_iso { final_dest="${BUILDDIR}/${dest}" final_dest_dir="$(dirname "${final_dest}")" - if [ ! -z "${md5}" ]; then - default_md5="$(basename "${dest}.md5")" - + if [ -n "${md5}" ]; then case "${md5}" in y | Y | yes | YES ) # Use a default name, in same dir as dest @@ -237,9 +238,7 @@ function copy_to_iso { ;; esac - md5="$(normalized_path "${md5}" "${final_md5}")" final_md5="${BUILDDIR}/${md5}" - final_md5_dir="$(dirname "${final_md5}")" fi if [ -z "${overwrite}" ] || [ "${overwrite}" == 'n' ]; then @@ -254,21 +253,14 @@ function copy_to_iso { exit 1 fi - if [ ! -z "${final_md5_dir}" ]; then - if [ ! -d "${final_md5_dir}" ]; then - log_error "Error: copy_to_iso: md5 destination directory does not exist '${final_md5_dir}'" - exit 1 - fi - fi - - \cp -f "${src}" "${final_dest}" + cp -f "${src}" "${final_dest}" if [ $? -ne 0 ]; then log_error "Error: Failed to copy '${src}' to '${final_dest}'" exit 1 fi - if [ ! -z "${final_md5}" ]; then - pushd ${final_dest_dir} > /dev/null + if [ -n "${final_md5}" ]; then + pushd "${final_dest_dir}" > /dev/null md5sum "$(basename "${final_dest}")" >> "${final_md5}" popd > /dev/null fi @@ -371,12 +363,13 @@ function find_in_patch { # make sure patch is an absolute path patch="$(abspath "${patch}")" - patchdir=$(mktemp -d -p $PWD updateiso_build_patch_XXXXXX) + patchdir=$(mktemp -d -p "${PWD}" updateiso_build_patch_XXXXXX) pushd "${patchdir}" > /dev/null extract_patch "${patch}" + # shellcheck disable=2044 for rpm in $(find . -name '*.rpm'); do if path="$(find_in_rpm "${rpm}" "${target}")"; then - found_rpm="$(basename ${rpm})" + found_rpm="$(basename "${rpm}")" break fi done @@ -411,14 +404,14 @@ function copy_rpm_file_to_iso { # make sure patch is an absolute path rpm="$(abspath "${rpm}")" - patchdir=$(mktemp -d -p $PWD updateiso_build_rpm_XXXXXX) + patchdir=$(mktemp -d -p "${PWD}" updateiso_build_rpm_XXXXXX) pushd "${patchdir}" > /dev/null - rpm2cpio "${rpm}" | cpio -imdv ${src} + rpm2cpio "${rpm}" | cpio -imdv "${src}" if [ $? -ne 0 ]; then - log_error "Error: copy_rpm_file_to_iso: extraction error from rpm '$(basename ${rpm})'" + log_error "copy_rpm_file_to_iso: extraction error from rpm '$(basename "${rpm}")'" rc=1 - elif [ ! -e ${src} ]; then - log_error "Error: copy_rpm_file_to_iso: file '${src}' not found in rpm '$(basename {rpm})'" + elif [ ! -e "${src}" ]; then + log_error "copy_rpm_file_to_iso: file '${src}' not found in rpm '$(basename "${rpm}")'" rc=1 else # we do not need an md5 here, so leaving third argument empty @@ -453,11 +446,11 @@ function copy_patch_file_to_iso { # make sure patch is an absolute path patch="$(abspath "${patch}")" - rpmdir=$(mktemp -d -p $PWD updateiso_build_patch_XXXXXX) + rpmdir=$(mktemp -d -p "${PWD}" updateiso_build_patch_XXXXXX) pushd "${rpmdir}" > /dev/null extract_patch "${patch}" if [ ! -f "${rpm}" ]; then - log_error "Error: copy_patch_file_to_iso: rpm '${rpm}' not found in patch '$(basename ${patch})'" + log_error "copy_patch_file_to_iso: rpm '${rpm}' not found in patch '$(basename "${patch}")'" rc=1 else copy_rpm_file_to_iso "${rpm}" "${src}" "${dest}" "${overwrite}" @@ -474,28 +467,31 @@ function copy_patch_file_to_iso { } function generate_boot_cfg { + log_info "Generating boot config" local isodir=$1 if [ -z "${EFI_MOUNT}" ]; then - mount_efiboot_img ${isodir} + mount_efiboot_img "${isodir}" fi local COMMON_ARGS="inst.text inst.gpt boot_device=sda rootfs_device=sda" COMMON_ARGS="${COMMON_ARGS} biosdevname=0 usbcore.autosuspend=-1" COMMON_ARGS="${COMMON_ARGS} security_profile=standard user_namespace.enable=1" COMMON_ARGS="${COMMON_ARGS} inst.stage2=hd:LABEL=${VOLUME_LABEL} inst.ks=hd:LABEL=${VOLUME_LABEL}:/${PRESTAGED_KICKSTART}" - if [[ "${FORCE_INSTALL}" == true ]]; then + if [ -n "${FORCE_INSTALL}" ]; then COMMON_ARGS="${COMMON_ARGS} force_install" fi + log_info "COMMON_ARGS: ${COMMON_ARGS}" - for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do - cat <<EOF > ${f} + for f in "${isodir}/isolinux.cfg" "${isodir}/syslinux.cfg"; do + cat <<EOF > "${f}" display splash.cfg timeout ${TIMEOUT} F1 help.txt F2 devices.txt F3 splash.cfg serial 0 115200 + ui vesamenu.c32 menu background #ff555555 default ${DEFAULT_SYSLINUX_ENTRY} @@ -521,7 +517,7 @@ menu end EOF done for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do - cat <<EOF > ${f} + cat <<EOF > "${f}" default=${DEFAULT_GRUB_ENTRY} timeout=${GRUB_TIMEOUT} search --no-floppy --set=root -l '${VOLUME_LABEL}' @@ -540,34 +536,46 @@ menuentry 'Graphical Console' --id=graphical { initrdefi /initrd.img } EOF - done } +# Source shared utility functions +DIR_NAME=$(dirname "$0") +if [ ! -e "${DIR_NAME}"/stx-iso-utils-centos.sh ]; then + echo "${DIR_NAME}/stx-iso-utils-centos.sh does not exist" >&2 + exit 1 +else + source "${DIR_NAME}"/stx-iso-utils-centos.sh +fi + +# Required variables declare INPUT_ISO= declare OUTPUT_ISO= declare -a IMAGES -declare ORIG_PWD=$PWD declare KS_SETUP= declare KS_ADDON= +declare UPDATE_TIMEOUT="no" +declare -i FOREVER_GRUB_TIMEOUT=-1 +declare -i DEFAULT_GRUB_TIMEOUT=30 +declare -i DEFAULT_TIMEOUT=$(( DEFAULT_GRUB_TIMEOUT*10 )) +declare -i TIMEOUT=${DEFAULT_TIMEOUT} +declare -i GRUB_TIMEOUT=${DEFAULT_GRUB_TIMEOUT} declare -a PARAMS declare -a PATCHES declare -a KICKSTART_PATCHES declare DEFAULT_LABEL= declare DEFAULT_SYSLINUX_ENTRY=1 declare DEFAULT_GRUB_ENTRY="graphical" -declare UPDATE_TIMEOUT="no" -declare FOREVER_GRUB_TIMEOUT=-1 -declare DEFAULT_GRUB_TIMEOUT=30 -declare -i DEFAULT_TIMEOUT=(DEFAULT_GRUB_TIMEOUT*10) -declare -i TIMEOUT=${DEFAULT_TIMEOUT} -declare GRUB_TIMEOUT=${DEFAULT_GRUB_TIMEOUT} +declare FORCE_INSTALL= declare PLATFORM_ROOT="opt/platform-backup" declare MD5_FILE="container-image.tar.gz.md5" declare VOLUME_LABEL="oe_prestaged_iso_boot" declare PRESTAGED_KICKSTART="prestaged_installer_ks.cfg" declare MENU_NAME="Prestaged Local Installer" -declare FORCE_INSTALL=false + +############################################################################### +# Get the command line arguments. +############################################################################### SHORTOPTS=""; LONGOPTS="" SHORTOPTS+="i:"; LONGOPTS+="input:," @@ -583,17 +591,26 @@ SHORTOPTS+="I:"; LONGOPTS+="images:," SHORTOPTS+="f"; LONGOPTS+="force-install," SHORTOPTS+="h"; LONGOPTS+="help" +declare -i rc OPTS=$(getopt -o "${SHORTOPTS}" --long "${LONGOPTS}" --name "$0" -- "$@") - if [ $? -ne 0 ]; then usage - exit 1 + log_fatal "Options to $0 not properly parsed" fi eval set -- "${OPTS}" +if [ $# = 1 ]; then + usage + log_fatal "No arguments were provided" +fi + while :; do case $1 in + -h | --help) + usage + exit 0 + ;; -i | --input) INPUT_ISO=$2 shift 2 @@ -611,24 +628,27 @@ while :; do shift 2 ;; -p | --param) + # shellcheck disable=2206 PARAMS+=(${2//,/ }) shift 2 ;; -P | --patch) + # shellcheck disable=2206 PATCHES+=(${2//,/ }) shift 2 ;; -K | --kickstart-patch) + # shellcheck disable=2206 KICKSTART_PATCHES+=(${2//,/ }) shift 2 ;; -I | --images) + # shellcheck disable=2206 IMAGES+=(${2//,/ }) shift 2 ;; -d | --default-boot) DEFAULT_LABEL=${2} - case ${DEFAULT_LABEL} in 0) DEFAULT_SYSLINUX_ENTRY=0 @@ -639,18 +659,16 @@ while :; do DEFAULT_GRUB_ENTRY="graphical" ;; *) - log_error "Invalid default boot menu option: ${DEFAULT_LABEL}" usage - exit 1 + log_fatal "Invalid default boot menu option: ${DEFAULT_LABEL}" ;; esac - shift 2 ;; -t | --timeout) - let -i timeout_arg=${2} - if [ ${timeout_arg} -gt 0 ]; then - let -i TIMEOUT=${timeout_arg}*10 + declare -i timeout_arg=${2} + if [ "${timeout_arg}" -gt 0 ]; then + TIMEOUT=$(( timeout_arg * 10 )) GRUB_TIMEOUT=${timeout_arg} elif [ ${timeout_arg} -eq 0 ]; then TIMEOUT=0 @@ -659,7 +677,6 @@ while :; do TIMEOUT=0 GRUB_TIMEOUT=${FOREVER_GRUB_TIMEOUT} fi - UPDATE_TIMEOUT="yes" shift 2 ;; @@ -673,111 +690,109 @@ while :; do ;; *) usage - exit 1 + log_fatal "Unexpected argument: $*" ;; esac done -if [ $# -ne 0 ]; then - log_error "Error: Unexpected arguments: $@" - usage - exit 1 -fi +############################################################################### +# Generate prestage iso. +# +############################################################################### + +log_info "Checking system requirements" check_requirements +## Check for mandatory parameters check_required_param "--input" "${INPUT_ISO}" check_required_param "--output" "${OUTPUT_ISO}" +# shellcheck disable=2068 check_files_exist ${INPUT_ISO} ${IMAGES[@]} ${PATCHES[@]} ${KICKSTART_PATCHES[@]} ${KS_SETUP} ${KS_ADDON} +# shellcheck disable=2068 check_files_size ${INPUT_ISO} ${IMAGES[@]} ${PATCHES[@]} ${KICKSTART_PATCHES[@]} ${KS_SETUP} ${KS_ADDON} -if [ -f ${OUTPUT_ISO} ]; then - log_error "Output file already exists: ${OUTPUT_ISO}" - exit 1 +if [ -e "${OUTPUT_ISO}" ]; then + log_fatal "${OUTPUT_ISO} exists. Delete before you execute this script." fi -shift $((OPTIND-1)) - +## Catch Control-C and handle. trap cleanup EXIT -BUILDDIR=$(mktemp -d -p $PWD updateiso_build_XXXXXX) -if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then - log_error "Failed to create builddir. Aborting..." - exit $rc +# Create a temporary build directory. +BUILDDIR=$(mktemp -d -p "${PWD}" updateiso_build_XXXXXX) +if [ -z "${BUILDDIR}" ] || [ ! -d "${BUILDDIR}" ]; then + log_fatal "Failed to create builddir. Aborting..." fi - -mount_iso ${INPUT_ISO} +log_info "Using BUILDDIR=${BUILDDIR}" +mount_iso "${INPUT_ISO}" # # prestaging kickstart # -# Verify prestaging kickstart is present, and where. An original 21.05 iso -# won't have the kickstart. It must be provided by a patch. -# KICKSTART_PATCHES take presedence over a platform PATCHES, which in -# turn take presedence over any content from the ISO. -PRESTAGED_KICKSTART_FOUND_IN="" -PRESTAGED_KICKSTART_PATH="" +# Verify prestaging kickstart is present, and where. +# KICKSTART_PATCHES take precedence over a platform PATCHES, which in +# turn take precedence over any content from the ISO. +PRESTAGED_KICKSTART_PATCH= +PRESTAGED_KICKSTART_PATH= -# scan patches last to first. -for PATCH in $(printf '%s\n' "${KICKSTART_PATCHES[@]}" | tac); do - if PRESTAGED_KICKSTART_PATH="$(find_in_patch "${PATCH}" "${PRESTAGED_KICKSTART}")" ; then - PRESTAGED_KICKSTART_FOUND_IN="${PATCH}" +# Scan KICKSTART_PATCHES last to first. +for patch in $(printf '%s\n' "${KICKSTART_PATCHES[@]}" | tac); do + if PRESTAGED_KICKSTART_PATH="$(find_in_patch "${patch}" "${PRESTAGED_KICKSTART}")" ; then + PRESTAGED_KICKSTART_PATCH="${patch}" break fi done -# scan patches last to first. We want to prefer the most recent patch. +# Scan PATCHES last to first. Prefer the most recent patch. # Assumes patches will be listed in order 0001, 0002, .... when given as args. -if [ -z "${PRESTAGED_KICKSTART_FOUND_IN}" ]; then - for PATCH in $(printf '%s\n' "${PATCHES[@]}" | tac); do - if PRESTAGED_KICKSTART_PATH="$(find_in_patch "${PATCH}" "${PRESTAGED_KICKSTART}")" ; then - PRESTAGED_KICKSTART_FOUND_IN="${PATCH}" +if [ -z "${PRESTAGED_KICKSTART_PATCH}" ]; then + for patch in $(printf '%s\n' "${PATCHES[@]}" | tac); do + if PRESTAGED_KICKSTART_PATH="$(find_in_patch "${patch}" "${PRESTAGED_KICKSTART}")" ; then + PRESTAGED_KICKSTART_PATCH="${patch}" break fi done fi -if [ -z "${PRESTAGED_KICKSTART_FOUND_IN}" ]; then +if [ -z "${PRESTAGED_KICKSTART_PATCH}" ]; then if PRESTAGED_KICKSTART_PATH="$(find_in_mounted_iso "${PRESTAGED_KICKSTART}")" ; then - PRESTAGED_KICKSTART_FOUND_IN="iso" + log_info "Using ${PRESTAGED_KICKSTART} from original ISO" + else + log_fatal "Failed to find required file '${PRESTAGED_KICKSTART}' in the supplied iso and patches." fi fi -if [ -z "${PRESTAGED_KICKSTART_FOUND_IN}" ]; then - log_error "Failed to find required file '${PRESTAGED_KICKSTART}' in the supplied iso and patches." - exit 1 -fi - # # Determine release version from ISO # -if [ ! -f ${MNTDIR}/upgrades/version ]; then +if [ ! -f "${MNTDIR}"/upgrades/version ]; then log_error "Version info not found on ${INPUT_ISO}" exit 1 fi -ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION}) +ISO_VERSION=$(source "${MNTDIR}/upgrades/version" && echo "${VERSION}") if [ -z "${ISO_VERSION}" ]; then log_error "Failed to determine version of installation ISO" exit 1 fi +# Copy the contents of the input iso to the build directory. -# -# copy content -# -rsync -a ${MNTDIR}/ ${BUILDDIR}/ +log_info "Copying input ISO" +rsync -av "${MNTDIR}/" "${BUILDDIR}/" rc=$? -if [ $rc -ne 0 ]; then - log_error "Call to rsync ISO content. Aborting..." - exit $rc +if [ "${rc}" -ne 0 ]; then + unmount_iso + log_fatal "Unable to rsync content from the ISO: Error rc=${rc}" fi -# copy kickstart -if [ "${PRESTAGED_KICKSTART_FOUND_IN}" != "iso" ]; then - copy_patch_file_to_iso "${PRESTAGED_KICKSTART_FOUND_IN}" "${PRESTAGED_KICKSTART_PATH%%:*}" "${PRESTAGED_KICKSTART_PATH##*:}" "/" "y" +# Copy kickstart if it is anywhere outside of the mounted ISO (otherwise it will already have been copied by the above) +if [ -n "${PRESTAGED_KICKSTART_PATCH}" ]; then + log_info "Prestaging kickstart from ${PRESTAGED_KICKSTART_PATCH}" + copy_patch_file_to_iso "${PRESTAGED_KICKSTART_PATCH}" "${PRESTAGED_KICKSTART_PATH%%:*}" "${PRESTAGED_KICKSTART_PATH##*:}" "/" "y" fi unmount_iso @@ -785,31 +800,31 @@ unmount_iso # # Setup syslinux and grub cfg files # -generate_boot_cfg ${BUILDDIR} +generate_boot_cfg "${BUILDDIR}" # # Set/update boot parameters # +log_info "Updating boot parameters" if [ ${#PARAMS[@]} -gt 0 ]; then - for p in ${PARAMS[@]}; do + for p in "${PARAMS[@]}"; do param=${p%%=*} # Strip from the first '=' on value=${p#*=} # Strip to the first '=' - - update_parameter ${BUILDDIR} "${param}" "${value}" + update_parameter "${BUILDDIR}" "${param}" "${value}" done fi if [ -n "${DEFAULT_LABEL}" ]; then - set_default_label ${BUILDDIR} + set_default_label "${BUILDDIR}" fi if [ "${UPDATE_TIMEOUT}" = "yes" ]; then - set_timeout ${BUILDDIR} + set_timeout "${BUILDDIR}" fi if [ -n "${KS_SETUP}" ]; then - \rm -f ${BUILDDIR}/ks-setup.cfg - \cp ${KS_SETUP} ${BUILDDIR}/ks-setup.cfg + \rm -f "${BUILDDIR}"/ks-setup.cfg + \cp "${KS_SETUP}" "${BUILDDIR}"/ks-setup.cfg if [ $? -ne 0 ]; then log_error "Error: Failed to copy ${KS_SETUP}" exit 1 @@ -817,8 +832,8 @@ if [ -n "${KS_SETUP}" ]; then fi if [ -n "${KS_ADDON}" ]; then - \rm -f ${BUILDDIR}/ks-addon.cfg - \cp ${KS_ADDON} ${BUILDDIR}/ks-addon.cfg + \rm -f "${BUILDDIR}"/ks-addon.cfg + \cp "${KS_ADDON}" "${BUILDDIR}"/ks-addon.cfg if [ $? -ne 0 ]; then log_error "Error: Failed to copy ${KS_ADDON}" exit 1 @@ -836,32 +851,36 @@ unmount_efiboot_img PLATFORM_PATH="${PLATFORM_ROOT}/${ISO_VERSION}" mkdir_on_iso "${PLATFORM_PATH}" -INPUT_ISO_NAME="$(basename "${INPUT_ISO}")" +INPUT_ISO_NAME="$(basename "${INPUT_ISO}")" copy_to_iso "${INPUT_ISO}" "${PLATFORM_PATH}/${INPUT_ISO_NAME}" "${PLATFORM_PATH}/${INPUT_ISO_NAME/%.iso/.md5}" -for PATCH in ${PATCHES[@]}; do - copy_to_iso "${PATCH}" "${PLATFORM_PATH}/" -done +if [ -n "${PATCHES[*]}" ]; then + log_info "Including patches: ${PATCHES[*]}" + for patch in "${PATCHES[@]}"; do + copy_to_iso "${patch}" "${PLATFORM_PATH}/" + done +fi -for IMAGE in ${IMAGES[@]}; do - copy_to_iso "${IMAGE}" "${PLATFORM_PATH}/" "${PLATFORM_PATH}/${MD5_FILE}" -done +if [ -n "${IMAGES[*]}" ]; then + log_info "Including images: ${IMAGES[*]}" + for IMAGE in "${IMAGES[@]}"; do + copy_to_iso "${IMAGE}" "${PLATFORM_PATH}/" "${PLATFORM_PATH}/${MD5_FILE}" + done +fi -# -# Rebuild the ISO -# -mkisofs -o ${OUTPUT_ISO} \ - -R -D -A "${VOLUME_LABEL}" -V "${VOLUME_LABEL}" \ - -quiet \ - -b isolinux.bin -c boot.cat -no-emul-boot \ - -boot-load-size 4 -boot-info-table \ - -eltorito-alt-boot \ - -e images/efiboot.img \ - -no-emul-boot \ - ${BUILDDIR} +# we are ready to create the prestage iso. +log_info "Creating ${OUTPUT_ISO}" +mkisofs -o "${OUTPUT_ISO}" \ + -R -D -A "${VOLUME_LABEL}" -V "${VOLUME_LABEL}" \ + -quiet \ + -b isolinux.bin -c boot.cat -no-emul-boot \ + -boot-load-size 4 -boot-info-table \ + -eltorito-alt-boot \ + -e images/efiboot.img \ + -no-emul-boot \ + "${BUILDDIR}" -isohybrid --uefi ${OUTPUT_ISO} -implantisomd5 ${OUTPUT_ISO} - -echo "Created ISO: ${OUTPUT_ISO}" +isohybrid --uefi "${OUTPUT_ISO}" +implantisomd5 "${OUTPUT_ISO}" +log_info "Prestage ISO created successfully: ${OUTPUT_ISO}" diff --git a/utilities/platform-util/scripts/gen-prestaged-iso.sh b/utilities/platform-util/scripts/gen-prestaged-iso.sh index bdd6d082..906afd2c 100755 --- a/utilities/platform-util/scripts/gen-prestaged-iso.sh +++ b/utilities/platform-util/scripts/gen-prestaged-iso.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2022 Wind River Systems, Inc. +# Copyright (c) 2022-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -14,29 +14,34 @@ # exceed 4 GB. Multiple archives can be provided. All archives # must have the suffix 'tar.gz'. +# shellcheck disable=1091 # don't warn about following 'source <file>' +# shellcheck disable=2164 # don't warn about pushd/popd failures +# shellcheck disable=2181 # don't warn about using rc=$? -# Error log print function log_fatal { - echo "ERROR: $@" >&2 && exit 1 + echo "$(date "+%F %H:%M:%S") FATAL: $*" >&2 + exit 1 } function log_error { - echo "ERROR: $@" >&2 + echo "$(date "+%F %H:%M:%S"): ERROR: $*" >&2 } -# Info log -function log { - echo "INFO: $@" >&2 +function log_warn { + echo "$(date "+%F %H:%M:%S"): WARN: $*" >&2 +} + +function log_info { + echo "$(date "+%F %H:%M:%S"): INFO: $*" >&2 } # Usage manual. function usage { cat <<ENDUSAGE -Utility to convert a StarlingX installation iso into a Debian prestaged -subcloud installation iso. +Utility to convert a StarlingX installation iso into a Debian prestaged subcloud installation iso. Usage: - $(basename $0) --input <input bootimage.iso> + $(basename "$0") --input <input bootimage.iso> --output <output bootimage.iso> [ --images <images.tar.gz> ] [ --patch <patch-name.patch> ] @@ -110,13 +115,14 @@ function normalized_path { local path="${1}" local default_fn="${2}" - local path_name="$(basename "${path}")" - local path_dir="$(dirname "${path}")" + local path_name path_dir + path_name="$(basename "${path}")" + path_dir="$(dirname "${path}")" # If 'path' ends in '/' then path was intended to be a directory - if [ "${path:(-1):1}" == "/" ]; then + if [ "${path: -1:1}" == "/" ]; then # Note: space is required after : to distinguish from ${path:-...} # Drop the trailing '/' - path_dir="${path:0:(-1)}" + path_dir="${path:0:-1}" path_name="${default_fn}" fi @@ -147,7 +153,6 @@ function copy_to_iso { local final_dest= local final_dest_dir= local final_md5= - local final_md5_dir= if [ -z "${src}" ] || [ -z "${dest}" ]; then log_error "Error: copy_to_iso: missing argument" @@ -164,8 +169,7 @@ function copy_to_iso { final_dest="${BUILDDIR}/${dest}" final_dest_dir="$(dirname "${final_dest}")" - if [ ! -z "${md5}" ]; then - + if [ -n "${md5}" ]; then case "${md5}" in y | Y | yes | YES ) # Use a default name, in same dir as dest @@ -194,47 +198,47 @@ function copy_to_iso { exit 1 fi - if [ ! -z "${final_md5}" ]; then - pushd ${final_dest_dir} > /dev/null + if [ -n "${final_md5}" ]; then + pushd "${final_dest_dir}" > /dev/null md5sum "$(basename "${final_dest}")" >> "${final_md5}" popd > /dev/null fi } function generate_boot_cfg { + log_info "Generating boot config" local isodir=$1 if [ -z "${EFI_MOUNT}" ]; then - mount_efiboot_img ${isodir} + mount_efiboot_img "${isodir}" fi local PARAM_LIST= - log "Generating prestage.iso from params: ${PARAMS[*]}" # Set/update boot parameters if [ ${#PARAMS[@]} -gt 0 ]; then + log_info "Pre-parsing params: ${PARAMS[*]}" for p in "${PARAMS[@]}"; do param=${p%%=*} value=${p#*=} # Pull the boot device out of PARAMS and convert to instdev - if [[ "${param}" == "boot_device" ]]; then - log "Setting instdev=${value} from boot_device param" + if [ "${param}" = "boot_device" ]; then + log_info "Setting instdev=${value} from boot_device param" instdev=${value} - elif [[ "${param}" == "rootfs_device" ]]; then - log "Setting instdev=${value} from boot_device param" + elif [ "${param}" = "rootfs_device" ]; then + log_info "Setting instdev=${value} from boot_device param" instdev=${value} fi PARAM_LIST="${PARAM_LIST} ${param}=${value}" done + log_info "Using parameters: ${PARAM_LIST}" fi - log "Parameters: ${PARAM_LIST}" - if [[ "${KS_PATCH}" == "true" ]]; then - log "Setting Kickstart patch from the kickstart_patches directory" + log_info "Setting Kickstart patch from the kickstart_patches directory" ks="${KICKSTART_PATCH_DIR}"/kickstart.cfg else - log "Setting Kickstart patch from the kickstart directory" + log_info "Setting Kickstart patch from the kickstart directory" ks=kickstart/kickstart.cfg fi @@ -248,14 +252,14 @@ function generate_boot_cfg { COMMON_ARGS="${COMMON_ARGS} inst_ostree_var=/dev/mapper/cgts--vg-var--lv" COMMON_ARGS="${COMMON_ARGS} defaultkernel=vmlinuz*[!t]-amd64" - if [[ -n "${FORCE_INSTALL}" ]]; then + if [ -n "${FORCE_INSTALL}" ]; then COMMON_ARGS="${COMMON_ARGS} force_install" fi # Uncomment for LAT debugging: #COMMON_ARGS="${COMMON_ARGS} instsh=2" COMMON_ARGS="${COMMON_ARGS} ${PARAM_LIST}" - log "COMMON_ARGS: $COMMON_ARGS" + log_info "COMMON_ARGS: ${COMMON_ARGS}" for f in ${isodir}/isolinux/isolinux.cfg; do cat <<EOF > "${f}" @@ -283,8 +287,7 @@ LABEL 1 append ${COMMON_ARGS} traits=controller console=tty0 EOF -done - + done for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do cat <<EOF > "${f}" default=${DEFAULT_GRUB_ENTRY} @@ -323,18 +326,19 @@ function generate_ostree_checkum { fi ( # subshell: - log "Calculating new checksum for ostree_repo at ${dest_dir}" + log_info "Calculating new checksum for ostree_repo at ${dest_dir}" cd "${dest_dir}" || log_fatal "generate_ostree_checkum: cd ${dest_dir} failed" find ostree_repo -type f -exec md5sum {} + | LC_ALL=C sort | md5sum | awk '{ print $1; }' \ > .ostree_repo_checksum - log "ostree_repo checksum: $(cat .ostree_repo_checksum)" + log_info "ostree_repo checksum: $(cat .ostree_repo_checksum)" ) } # Constants DIR_NAME=$(dirname "$0") -if [[ ! -e "${DIR_NAME}"/stx-iso-utils.sh ]]; then - log_fatal "${DIR_NAME}/stx-iso-utils.sh does not exist" +if [ ! -e "${DIR_NAME}"/stx-iso-utils.sh ]; then + echo "${DIR_NAME}/stx-iso-utils.sh does not exist" >&2 + exit 1 else source "${DIR_NAME}"/stx-iso-utils.sh fi @@ -343,13 +347,12 @@ fi declare INPUT_ISO= declare OUTPUT_ISO= declare -a IMAGES -declare ORIG_PWD=$PWD declare KS_SETUP= declare KS_ADDON= declare UPDATE_TIMEOUT="no" declare -i FOREVER_GRUB_TIMEOUT=-1 declare -i DEFAULT_GRUB_TIMEOUT=30 -declare -i DEFAULT_TIMEOUT=(DEFAULT_GRUB_TIMEOUT*10) +declare -i DEFAULT_TIMEOUT=$(( DEFAULT_GRUB_TIMEOUT*10 )) declare -i TIMEOUT=${DEFAULT_TIMEOUT} declare -i GRUB_TIMEOUT=${DEFAULT_GRUB_TIMEOUT} declare -a PARAMS @@ -381,97 +384,109 @@ SHORTOPTS+="I:"; LONGOPTS+="images:," SHORTOPTS+="f"; LONGOPTS+="force-install," SHORTOPTS+="h"; LONGOPTS+="help" +declare -i rc OPTS=$(getopt -o "${SHORTOPTS}" --long "${LONGOPTS}" --name "$0" -- "$@") -if [[ "$?" -ne 0 ]]; then +if [ $? -ne 0 ]; then usage log_fatal "Options to $0 not properly parsed" fi eval set -- "${OPTS}" -if [[ $# == 1 ]]; then +if [ $# = 1 ]; then usage log_fatal "No arguments were provided" fi while :; do case $1 in - -i | --input) - INPUT_ISO="$2" - shift 2 - ;; - -o | --output) - OUTPUT_ISO=$2 - shift 2 - ;; - -s | --setup) - KS_SETUP=$2 - shift 2 - ;; - -a | --addon) - KS_ADDON=$2 - shift 2 - ;; - -p | --param) - PARAMS+=(${2//,/ }) - shift 2 - ;; - -P | --patch) - PATCHES+=(${2//,/ }) - shift 2 - ;; - -K | --kickstart-patch) - KICKSTART_PATCHES+=(${2//,/ }) - shift 2 - ;; - -I | --images) - IMAGES+=(${2//,/ }) - shift 2 - ;; - -d | --default-boot) - DEFAULT_LABEL=$2 - case ${DEFAULT_LABEL} in - 0) - DEFAULT_SYSLINUX_ENTRY=0 - DEFAULT_GRUB_ENTRY="serial" - ;; - 1) - DEFAULT_SYSLINUX_ENTRY=1 - DEFAULT_GRUB_ENTRY="graphical" - ;; - *) - usage - log_fatal "Invalid default boot menu option: ${DEFAULT_LABEL}" - ;; - esac - shift 2 - ;; - -t | --timeout) - let -i timeout_arg=$2 - if [[ "${timeout_arg}" -gt 0 ]]; then - let -i "TIMEOUT=${timeout_arg}*10" - GRUB_TIMEOUT="${timeout_arg}" - elif [[ "${timeout_arg}" -eq 0 ]]; then - TIMEOUT=0 - GRUB_TIMEOUT=0.001 - elif [[ "${timeout_arg}" -lt 0 ]]; then - TIMEOUT=0 - GRUB_TIMEOUT=${FOREVER_GRUB_TIMEOUT} - fi - UPDATE_TIMEOUT="yes" - shift 2 - ;; - -f | --force-install) - FORCE_INSTALL="true" - shift - ;; - --) - break - ;; - *) - shift - break - ;; + -h | --help) + usage + exit 0 + ;; + -i | --input) + INPUT_ISO=$2 + shift 2 + ;; + -o | --output) + OUTPUT_ISO=$2 + shift 2 + ;; + -s | --setup) + KS_SETUP=$2 + shift 2 + ;; + -a | --addon) + KS_ADDON=$2 + shift 2 + ;; + -p | --param) + # shellcheck disable=2206 + PARAMS+=(${2//,/ }) + shift 2 + ;; + # TODO(kmacleod) Does providing patches make sense? + -P | --patch) + # shellcheck disable=2206 + PATCHES+=(${2//,/ }) + shift 2 + ;; + -K | --kickstart-patch) + # shellcheck disable=2206 + KICKSTART_PATCHES+=(${2//,/ }) + shift 2 + ;; + -I | --images) + # shellcheck disable=2206 + IMAGES+=(${2//,/ }) + shift 2 + ;; + -d | --default-boot) + DEFAULT_LABEL=${2} + case ${DEFAULT_LABEL} in + 0) + DEFAULT_SYSLINUX_ENTRY=0 + DEFAULT_GRUB_ENTRY="serial" + ;; + 1) + DEFAULT_SYSLINUX_ENTRY=1 + DEFAULT_GRUB_ENTRY="graphical" + ;; + *) + usage + log_fatal "Invalid default boot menu option: ${DEFAULT_LABEL}" + ;; + esac + shift 2 + ;; + -t | --timeout) + declare -i timeout_arg=${2} + if [ "${timeout_arg}" -gt 0 ]; then + TIMEOUT=$(( timeout_arg * 10 )) + GRUB_TIMEOUT=${timeout_arg} + elif [ ${timeout_arg} -eq 0 ]; then + TIMEOUT=0 + GRUB_TIMEOUT=0.001 + elif [ ${timeout_arg} -lt 0 ]; then + TIMEOUT=0 + GRUB_TIMEOUT=${FOREVER_GRUB_TIMEOUT} + fi + # TODO(kmacleod): UPDATE_TIMEOUT is not used, why is that? + UPDATE_TIMEOUT="yes" + shift 2 + ;; + -f | --force-install) + FORCE_INSTALL=true + shift + ;; + --) + shift + break + ;; + *) + usage + log_fatal "Unexpected argument: $*" + ;; esac done @@ -480,51 +495,78 @@ done # Generate prestage iso. # ############################################################################### + +log_info "Checking system requirements" check_requirements ## Check for mandatory parameters check_required_param "--input" "${INPUT_ISO}" check_required_param "--output" "${OUTPUT_ISO}" +# shellcheck disable=2068 check_files_exist ${INPUT_ISO} ${PATCHES[@]} ${IMAGES[@]} ${KS_SETUP} ${KS_ADDON} ${KICKSTART_PATCHES[@]} +# shellcheck disable=2068 check_files_size ${PATCHES[@]} ${IMAGES[@]} ${KS_SETUP} ${KS_ADDON} ${KICKSTART_PATCHES[@]} -if [[ -e "${OUTPUT_ISO}" ]]; then +if [ -e "${OUTPUT_ISO}" ]; then log_fatal "${OUTPUT_ISO} exists. Delete before you execute this script." fi +# Check for rootfs_device/boot_device and warn if not present +found_rootfs_device= +found_boot_device= +if [ ${#PARAMS[@]} -gt 0 ]; then + for p in "${PARAMS[@]}"; do + param=${p%%=*} + case "${param}" in + rootfs_device) + found_rootfs_device=1 + ;; + boot_device) + found_boot_device=1 + ;; + esac + done +fi +if [ -z "${found_rootfs_device}" ]; then + log_warn "Missing '--param rootfs_device=...'. A default device will be selected during install, which may not be desired" +fi +if [ -z "${found_boot_device}" ]; then + log_warn "Missing '--param boot_device=...'. A default device will be selected during install, which may not be desired" +fi ## Catch Control-C and handle. trap cleanup EXIT # Create a temporary build directory. -BUILDDIR=$(mktemp -d -p $PWD updateiso_build_XXXXXX) -if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then +BUILDDIR=$(mktemp -d -p "${PWD}" updateiso_build_XXXXXX) +if [ -z "${BUILDDIR}" ] || [ ! -d "${BUILDDIR}" ]; then log_fatal "Failed to create builddir. Aborting..." fi -echo ${BUILDDIR} +log_info "Using BUILDDIR=${BUILDDIR}" mount_iso "${INPUT_ISO}" # # Determine release version from ISO # -if [ ! -f ${MNTDIR}/upgrades/version ]; then +if [ ! -f "${MNTDIR}"/upgrades/version ]; then log_error "Version info not found on ${INPUT_ISO}" exit 1 fi -ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION}) +ISO_VERSION=$(source "${MNTDIR}/upgrades/version" && echo "${VERSION}") if [ -z "${ISO_VERSION}" ]; then log_error "Failed to determine version of installation ISO" exit 1 fi # Copy the contents of the input iso to the build directory. -# This ensures that the ostree, kernel and the initramfs are all copied over +# This ensures that the ostree_repo, kernel and the initramfs are all copied over # to the prestage iso. +log_info "Copying input ISO" rsync -a --exclude "pxeboot" "${MNTDIR}/" "${BUILDDIR}/" rc=$? -if [[ "${rc}" -ne 0 ]]; then +if [ "${rc}" -ne 0 ]; then unmount_iso log_fatal "Unable to rsync content from the ISO: Error rc=${rc}" fi @@ -541,27 +583,32 @@ unmount_iso PLATFORM_PATH="${PLATFORM_ROOT}/${ISO_VERSION}" mkdir_on_iso "${PLATFORM_PATH}" -for PATCH in ${PATCHES[@]}; do - copy_to_iso "${PATCH}" "${PLATFORM_PATH}/" -done +if [ -n "${PATCHES[*]}" ]; then + log_info "Including patches: ${PATCHES[*]}" + for PATCH in "${PATCHES[@]}"; do + copy_to_iso "${PATCH}" "${PLATFORM_PATH}/" + done +fi -for IMAGE in ${IMAGES[@]}; do - copy_to_iso "${IMAGE}" "${PLATFORM_PATH}/" "${PLATFORM_PATH}/${MD5_FILE}" -done +if [ -n "${IMAGES[*]}" ]; then + log_info "Including images: ${IMAGES[*]}" + for IMAGE in "${IMAGES[@]}"; do + copy_to_iso "${IMAGE}" "${PLATFORM_PATH}/" "${PLATFORM_PATH}/${MD5_FILE}" + done +fi KICKSTART_PATCH_DIR="kickstart_patch" mkdir_on_iso "${KICKSTART_PATCH_DIR}" -for PATCH in ${KICKSTART_PATCHES[@]}; do - log "Found kickstart patch" +for PATCH in "${KICKSTART_PATCHES[@]}"; do + log_info "Including kickstart patch: ${PATCH}" copy_to_iso "${PATCH}" "${KICKSTART_PATCH_DIR}" KS_PATCH="true" done # generate the grub and isolinux cmd line parameters - generate_boot_cfg "${BUILDDIR}" -# copy the addon and setup files to the BUILDDIR +# copy the addon and setup files to the BUILDDIR if [[ -e "${KS_SETUP}" ]]; then cp "${KS_SETUP}" "${BUILDDIR}" fi @@ -584,4 +631,4 @@ mkisofs -o "${OUTPUT_ISO}" \ isohybrid --uefi "${OUTPUT_ISO}" -log "Prestage ISO created successfully" +log_info "Prestage ISO created successfully: ${OUTPUT_ISO}" diff --git a/utilities/platform-util/scripts/stx-iso-utils-centos.sh b/utilities/platform-util/scripts/stx-iso-utils-centos.sh index 2e1ff42d..a3fe127b 100644 --- a/utilities/platform-util/scripts/stx-iso-utils-centos.sh +++ b/utilities/platform-util/scripts/stx-iso-utils-centos.sh @@ -1,31 +1,49 @@ #!/bin/bash # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # # Common bash utility functions for StarlingX ISO tools # +# Global shellcheck ignores: +# shellcheck disable=2181 declare BUILDDIR= declare EFIBOOT_IMG_LOOP= declare EFI_MOUNT= declare MNTDIR= declare WORKDIR= +declare VERBOSE= + +function ilog { + echo "$(date "+%F %H:%M:%S"): $*" >&2 +} + +function elog { + echo "$(date "+%F %H:%M:%S") Error: $*" >&2 + exit 1 +} + +function vlog { + [ "${VERBOSE}" = true ] && echo "$(date "+%F %H:%M:%S"): $*" >&2 +} function common_cleanup { unmount_efiboot_img - if [ -n "$MNTDIR" -a -d "$MNTDIR" ]; then + if [ -n "${MNTDIR}" ] && [ -d "${MNTDIR}" ]; then unmount_iso fi - if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then - \rm -rf $BUILDDIR + if [ -n "${BUILDDIR}" ] && [ -d "${BUILDDIR}" ]; then + chmod -R 755 "${BUILDDIR}" + rm -rf "${BUILDDIR}" fi - if [ -n "$WORKDIR" -a -d "$WORKDIR" ]; then - \rm -rf $WORKDIR + if [ -n "${WORKDIR}" ] && [ -d "${WORKDIR}" ]; then + chmod -R 755 "${WORKDIR}" + rm -rf "${WORKDIR}" fi } @@ -58,27 +76,26 @@ function common_check_requirements { ) fi - required_utils+=( $@ ) + required_utils+=( "$@" ) local -i missing=0 - which which >&/dev/null - if [ $? -ne 0 ]; then - log_error "Unable to find 'which' utility. Aborting..." - exit 1 + if which which >&/dev/null -ne 0 ; then + elog "unable to find 'which' utility. Aborting..." fi - for req in ${required_utils[@]}; do - which ${req} >&/dev/null - if [ $? -ne 0 ]; then - log_error "Unable to find required utility: ${req}" - let -i missing++ + for req in "${required_utils[@]}"; do + which "${req}" >&/dev/null + if [ $? -ne 0 ] ; then + ilog "unable to find required utility: ${req}" + (( missing++ )) fi done - if [ ${missing} -gt 0 ]; then - log_error "One or more required utilities are missing. Aborting..." - exit 1 + if [ "${missing}" -gt 0 ]; then + elog "one or more required utilities are missing. Aborting..." + else + ilog "all required iso utilities present" fi } @@ -87,16 +104,14 @@ function check_required_param { local value="${2}" if [ -z "${value}" ]; then - log_error "Required parameter ${param} is not set" - exit 1 + elog "required parameter ${param} is not set" fi } function check_files_exist { for value in "$@"; do if [ ! -f "${value}" ]; then - log_error "file path '${value}' is invalid" - exit 1 + elog "file path '${value}' is invalid" fi done } @@ -108,10 +123,9 @@ function check_files_size { local file_size_limit=4000000000 for value in "$@"; do - file_size=$(stat --printf="%s" ${value}) - if [ ${file_size} -gt ${file_size_limit} ]; then - log_error "file size of '${value}' exceeds 4 GB limit" - exit 1 + file_size=$(stat --printf="%s" "${value}") + if [ "${file_size}" -ge "${file_size_limit}" ]; then + elog "file size of '${value}' exceeds 4 GB limit" fi done } @@ -119,54 +133,61 @@ function check_files_size { function mount_iso { local input_iso=$1 local basedir=${2:-$PWD} + local guestmount_dev=${3:-"/dev/sda1"} MNTDIR=$(mktemp -d -p "$basedir" stx-iso-utils_mnt_XXXXXX) - if [ -z "${MNTDIR}" -o ! -d ${MNTDIR} ]; then - log_error "Failed to create mntdir $MNTDIR. Aborting..." - exit 1 + if [ -z "${MNTDIR}" ] || [ ! -d "${MNTDIR}" ]; then + elog "Failed to create mntdir $MNTDIR. Aborting..." fi + ilog "mount_iso input_iso=${input_iso} basedir=${basedir} MNTDIR=${MNTDIR}" if [ $UID -eq 0 ]; then # Mount the ISO - mount -o loop ${input_iso} ${MNTDIR} + ilog "mounting ${input_iso} to ${MNTDIR}" + mount -o loop "${input_iso}" "${MNTDIR}" >&/dev/null if [ $? -ne 0 ]; then - echo "Failed to mount ${input_iso}" >&2 - exit 1 + elog "Failed to mount ${input_iso}" >&2 fi else # As non-root user, mount the ISO using guestmount - guestmount -a ${input_iso} -m /dev/sda1 --ro ${MNTDIR} - rc=$? - if [ $rc -ne 0 ]; then - # Add a retry - echo "Call to guestmount failed with rc=$rc. Retrying once..." + ilog "guestmounting ${input_iso} using ${guestmount_dev} to ${MNTDIR}" - guestmount -a ${input_iso} -m /dev/sda1 --ro ${MNTDIR} + guestmount -a "${input_iso}" -m "${guestmount_dev}" --ro "${MNTDIR}" + rc=$? + if [ "${rc}" -ne 0 ]; then + # Add a retry + ilog "guestmount failed with rc=${rc}. Retrying once..." + + guestmount -a "${input_iso}" -m "${guestmount_dev}" --ro "${MNTDIR}" >&/dev/null rc=$? - if [ $rc -ne 0 ]; then - echo "Call to guestmount failed with rc=$rc. Aborting..." - exit $rc + if [ ${rc} -ne 0 ]; then + elog "guestmount retry failed with rc=$rc. Aborting..." + else + vlog "guestmount retry succeeded" fi + else + vlog "guestmount succeeded." fi fi } function unmount_iso { - if [ $UID -eq 0 ]; then - umount ${MNTDIR} + if [ "${UID}" -eq 0 ]; then + ilog "unmounting ${MNTDIR}" + umount "${MNTDIR}" >&/dev/null else - guestunmount ${MNTDIR} + guestunmount "${MNTDIR}" >&/dev/null fi - rmdir ${MNTDIR} + rmdir "${MNTDIR}" } function mount_efiboot_img { local isodir=$1 - if [ -e ${isodir}/images/efiboot.img ]; then - local efiboot_img=${isodir}/images/efiboot.img + if [ -e "${isodir}/images/efiboot.img" ]; then + local efiboot_img="${isodir}/images/efiboot.img" else - local efiboot_img=${isodir}/efi.img + local efiboot_img="${isodir}/efi.img" fi local loop_setup_output= @@ -174,45 +195,42 @@ function mount_efiboot_img { if [ $UID -eq 0 ]; then # As root, setup a writeable loop device for the # efiboot.img file and mount it - loop_setup_output=$(losetup --show -f ${efiboot_img}) + loop_setup_output=$(losetup --show -f "${efiboot_img}") if [ $? -ne 0 ]; then - echo "Failed losetup" >&2 - exit 1 + elog "Failed losetup" >&2 fi EFIBOOT_IMG_LOOP=${loop_setup_output} EFI_MOUNT=$(mktemp -d -p /mnt -t EFI-noudev.XXXXXX) - mount ${EFIBOOT_IMG_LOOP} ${EFI_MOUNT} + mount "${EFIBOOT_IMG_LOOP}" "${EFI_MOUNT}" if [ $? -ne 0 ]; then - echo "Failed to mount loop device ${EFIBOOT_IMG_LOOP}" >&2 - exit 1 + elog "Failed to mount loop device ${EFIBOOT_IMG_LOOP}" >&2 fi else # As non-root user, we can use the udisksctl to setup a loop device # and mount the efiboot.img, with read/write access. - loop_setup_output=$(udisksctl loop-setup -f ${efiboot_img} --no-user-interaction) + loop_setup_output=$(udisksctl loop-setup -f "${efiboot_img}" --no-user-interaction) if [ $? -ne 0 ]; then - echo "Failed udisksctl loop-setup" >&2 - exit 1 + elog "Failed udisksctl loop-setup" >&2 fi - EFIBOOT_IMG_LOOP=$(echo ${loop_setup_output} | awk '{print $5;}' | sed -e 's/\.$//g') + EFIBOOT_IMG_LOOP=$(echo "${loop_setup_output}" | awk '{print $5;}' | sed -e 's/\.$//g') if [ -z "${EFIBOOT_IMG_LOOP}" ]; then - echo "Failed to determine loop device from command output:" >&2 + echo "Error: Failed to determine loop device from command output:" >&2 echo "${loop_setup_output}" >&2 exit 1 fi - udisksctl mount -b ${EFIBOOT_IMG_LOOP} + udisksctl mount -b "${EFIBOOT_IMG_LOOP}" >&/dev/null if [ $? -ne 0 ]; then - echo "Failed udisksctl mount" >&2 + echo "Error: Failed udisksctl mount" >&2 exit 1 fi - EFI_MOUNT=$(udisksctl info -b ${EFIBOOT_IMG_LOOP} | grep MountPoints | awk '{print $2;}') + EFI_MOUNT=$(udisksctl info -b "${EFIBOOT_IMG_LOOP}" | grep MountPoints | awk '{print $2;}') if [ -z "${EFI_MOUNT}" ]; then - echo "Failed to determine mount point from udisksctl info command" >&2 + echo "Error: Failed to determine mount point from udisksctl info command" >&2 exit 1 fi fi @@ -221,19 +239,19 @@ function mount_efiboot_img { function unmount_efiboot_img { if [ $UID -eq 0 ]; then if [ -n "${EFI_MOUNT}" ]; then - mountpoint -q ${EFI_MOUNT} && umount ${EFI_MOUNT} - rmdir ${EFI_MOUNT} + mountpoint -q "${EFI_MOUNT}" && umount "${EFI_MOUNT}" + rmdir "${EFI_MOUNT}" EFI_MOUNT= fi if [ -n "${EFIBOOT_IMG_LOOP}" ]; then - losetup -d ${EFIBOOT_IMG_LOOP} + losetup -d "${EFIBOOT_IMG_LOOP}" EFIBOOT_IMG_LOOP= fi else if [ -n "${EFIBOOT_IMG_LOOP}" ]; then - udisksctl unmount -b ${EFIBOOT_IMG_LOOP} - udisksctl loop-delete -b ${EFIBOOT_IMG_LOOP} + udisksctl unmount -b "${EFIBOOT_IMG_LOOP}" >&/dev/null + udisksctl loop-delete -b "${EFIBOOT_IMG_LOOP}" EFI_MOUNT= EFIBOOT_IMG_LOOP= fi @@ -245,44 +263,41 @@ function update_parameter { local param=$2 local value=$3 + ilog "updating parameter ${param} to ${param}=${value}" if [ -z "${EFI_MOUNT}" ]; then - mount_efiboot_img ${isodir} + mount_efiboot_img "${isodir}" fi - for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do - grep -q "^[[:space:]]*append\>.*[[:space:]]${param}=" ${f} + for f in "${isodir}"/isolinux.cfg "${isodir}"/syslinux.cfg ; do + grep -q "^[[:space:]]*append\>.*[[:space:]]${param}=" "${f}" if [ $? -eq 0 ]; then # Parameter already exists. Update the value - sed -i -e "s#^\([[:space:]]*append\>.*${param}\)=[^[:space:]]*#\1=${value}#" ${f} + sed -i -e "s#^\([[:space:]]*append\>.*${param}\)=[^[:space:]]*#\1=${value}#" "${f}" if [ $? -ne 0 ]; then - log_error "Failed to update parameter ($param)" - exit 1 + elog "Failed to update parameter ($param)" fi else # Parameter doesn't exist. Add it to the cmdline - sed -i -e "s|^\([[:space:]]*append\>.*\)|\1 ${param}=${value}|" ${f} + sed -i -e "s|^\([[:space:]]*append\>.*\)|\1 ${param}=${value}|" "${f}" if [ $? -ne 0 ]; then - log_error "Failed to add parameter ($param)" - exit 1 + elog "Failed to add parameter ($param)" fi fi done - for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do - grep -q "^[[:space:]]*linuxefi\>.*[[:space:]]${param}=" ${f} + for f in "${isodir}/EFI/BOOT/grub.cfg" "${EFI_MOUNT}/EFI/BOOT/grub.cfg" ; do + grep -q "^[[:space:]]*linuxefi\>.*[[:space:]]${param}=" "${f}" if [ $? -eq 0 ]; then # Parameter already exists. Update the value - sed -i -e "s#^\([[:space:]]*linuxefi\>.*${param}\)=[^[:space:]]*#\1=${value}#" ${f} + sed -i -e "s#^\([[:space:]]*linuxefi\>.*${param}\)=[^[:space:]]*#\1=${value}#" "${f}" if [ $? -ne 0 ]; then - log_error "Failed to update parameter ($param)" - exit 1 + elog "Failed to update parameter ($param)" fi else # Parameter doesn't exist. Add it to the cmdline - sed -i -e "s|^\([[:space:]]*linuxefi\>.*\)|\1 ${param}=${value}|" ${f} + sed -i -e "s|^\([[:space:]]*linuxefi\>.*\)|\1 ${param}=${value}|" "${f}" if [ $? -ne 0 ]; then - log_error "Failed to add parameter ($param)" - exit 1 + elog "Failed to add parameter ($param)" fi fi done diff --git a/utilities/platform-util/scripts/stx-iso-utils.sh b/utilities/platform-util/scripts/stx-iso-utils.sh index 9e6c3bfa..cafba26f 100644 --- a/utilities/platform-util/scripts/stx-iso-utils.sh +++ b/utilities/platform-util/scripts/stx-iso-utils.sh @@ -1,29 +1,32 @@ #!/bin/bash # -# Copyright (c) 2020,2022 Wind River Systems, Inc. +# Copyright (c) 2020-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # # Common bash utility functions for StarlingX ISO tools # +# Global shellcheck ignores: +# shellcheck disable=2181 declare BUILDDIR= declare EFIBOOT_IMG_LOOP= declare EFI_MOUNT= declare MNTDIR= declare WORKDIR= +declare VERBOSE= function ilog { - echo "$(date "+%F %H-%M-%S"): $*" >&2 + echo "$(date "+%F %H:%M:%S"): $*" >&2 } function elog { - echo "$(date "+%F %H-%M-%S") Error: $*" >&2 + echo "$(date "+%F %H:%M:%S") Error: $*" >&2 exit 1 } function vlog { - [ "${VERBOSE}" = true ] && echo "$(date "+%F %H-%M-%S"): $*" >&2 + [ "${VERBOSE}" = true ] && echo "$(date "+%F %H:%M:%S"): $*" >&2 } function common_cleanup { @@ -130,12 +133,13 @@ function check_files_size { function mount_iso { local input_iso=$1 local basedir=${2:-$PWD} - local mount=${3:-"/dev/sda1"} + local guestmount_dev=${3:-"/dev/sda1"} MNTDIR=$(mktemp -d -p "$basedir" stx-iso-utils_mnt_XXXXXX) if [ -z "${MNTDIR}" ] || [ ! -d "${MNTDIR}" ]; then elog "Failed to create mntdir $MNTDIR. Aborting..." fi + ilog "mount_iso input_iso=${input_iso} basedir=${basedir} MNTDIR=${MNTDIR}" if [ $UID -eq 0 ]; then # Mount the ISO @@ -146,15 +150,15 @@ function mount_iso { fi else # As non-root user, mount the ISO using guestmount - ilog "guestmounting ${input_iso} using ${mount} to ${MNTDIR}" + ilog "guestmounting ${input_iso} using ${guestmount_dev} to ${MNTDIR}" - guestmount -a "${input_iso}" -m "${mount}" --ro "${MNTDIR}" + guestmount -a "${input_iso}" -m "${guestmount_dev}" --ro "${MNTDIR}" rc=$? if [ "${rc}" -ne 0 ]; then # Add a retry ilog "guestmount failed with rc=${rc}. Retrying once..." - guestmount -a "${input_iso}" -m "${mount}" --ro "${MNTDIR}" >&/dev/null + guestmount -a "${input_iso}" -m "${guestmount_dev}" --ro "${MNTDIR}" >&/dev/null rc=$? if [ ${rc} -ne 0 ]; then elog "guestmount retry failed with rc=$rc. Aborting..." @@ -286,7 +290,7 @@ function update_parameter { fi done - for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg ; do + for f in "${isodir}/EFI/BOOT/grub.cfg" "${EFI_MOUNT}/EFI/BOOT/grub.cfg" ; do grep -q "^[[:space:]]*linux\>.*[[:space:]]${param}=" "${f}" if [ $? -eq 0 ]; then # Parameter already exists. Update the value diff --git a/utilities/platform-util/scripts/test/.gitignore b/utilities/platform-util/scripts/test/.gitignore new file mode 100644 index 00000000..56cccd49 --- /dev/null +++ b/utilities/platform-util/scripts/test/.gitignore @@ -0,0 +1,3 @@ +/input +/output +/shunit2 diff --git a/utilities/platform-util/scripts/test/README.md b/utilities/platform-util/scripts/test/README.md new file mode 100644 index 00000000..3a568d47 --- /dev/null +++ b/utilities/platform-util/scripts/test/README.md @@ -0,0 +1,24 @@ +Testing ISO generation scripts +============================== + +This directory contains unit test cases for the `gen-*.sh` scripts: + + +## Running tests + +Run all available tests via the `run-tests.sh` script. +You can also run the individual `gen-*-test.sh` scripts manually. + +The shunit2 bash unit test framework is used. It is automatically retrieved if it doesn't exist. + + +## Directories + +Directories used by the tests: + +input/ + - Contents of ISO/image/patch files used for the tests + - The contents of this directory are either downloaded or generated by the tests + +output/ + - Temporary test output directory diff --git a/utilities/platform-util/scripts/test/gen-prestaged-iso-centos-test.sh b/utilities/platform-util/scripts/test/gen-prestaged-iso-centos-test.sh new file mode 100755 index 00000000..51087252 --- /dev/null +++ b/utilities/platform-util/scripts/test/gen-prestaged-iso-centos-test.sh @@ -0,0 +1,300 @@ +#!/bin/bash +# vim:ft=sh:sts=4:sw=4 + +# This is a shunit2 test file. +# See https://github.com/kward/shunit2 +# Run the tests by executing this script. + +# shellcheck disable=SC2016 +# shellcheck disable=SC1090 +# shellcheck disable=SC1091 + +NAME=gen-prestaged-iso-centos-test + +# shellcheck disable=SC2155,SC2034 +readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") +# shellcheck disable=SC2155,SC2034 +readonly TARGET_SCRIPTDIR=$(readlink -m "${SCRIPTDIR}/..") + +INPUT_DIR="${SCRIPTDIR}"/input/centos +IMAGES_DIR="${INPUT_DIR}/images" +PATCHES_DIR="${INPUT_DIR}/patches" +ISOFILE=${ISOFILE:-$INPUT_DIR/bootimage.iso} +#ISOFILE=/localdisk/designer/kmacleod/dc-libvirt/isofiles/wrcp-22.12-release/starlingx-intel-x86-64-cd.iso +#ISOFILE=/localdisk/designer/kmacleod/dc-libvirt/isofiles/WRCP-21.12-formal-patch/bootimage.iso + +# source the script under test +. "${TARGET_SCRIPTDIR}"/stx-iso-utils-centos.sh +. "${SCRIPTDIR}/shunit2_helper.sh" + +KEEP_ARTIFACTS=${KEEP_ARTIFACTS:-} +BUILDDIR=${SCRIPTDIR}/output/${NAME} +OUTPUT_ISO=${BUILDDIR}/generated.iso + +_create_fake_image() { + local imagename=$1 + local targetdir=$2 + if hash docker 2>/dev/null; then + echo "Creating fake image ${imagename} using docker" + tar cv --files-from /dev/null | docker import - "${imagename}:latest" + docker save -o "${targetdir}/${imagename}.tar.gz" "${imagename}:latest" + docker rmi "${imagename}:latest" + else + echo "Creating fake empty image ${imagename}" + touch "${targetdir}/${imagename}.tar.gz" + fi +} + +_create_fake_images() { + if [ ! -d "${IMAGES_DIR}" ]; then + echo "Creating fake images" + mkdir -p "${IMAGES_DIR}" || fail "mkdir failed" + local image + for image in image1 image2 image3; do + _create_fake_image "${image}" "${IMAGES_DIR}" + done + fi +} + +_fetch_iso_and_patches() { + # Fetch CentOS ISO and test patches from yow-cgts4-lx + [ -d "${INPUT_DIR}" ] || mkdir -p "${INPUT_DIR}" || fail "mkdir failed" + if [ ! -f "${INPUT_DIR}/bootimage.iso" ]; then + echo "Fetching ISO" + scp 'yow-cgts4-lx:/localdisk/loadbuild/jenkins/WRCP_21.12_Build/last_build_with_test_patches/export/bootimage.{iso,sig}' "${INPUT_DIR}/" + fi + if [ ! -d "${PATCHES_DIR}" ]; then + echo "Fetching patches" + mkdir -p "${PATCHES_DIR}" || fail "mkdir failed" + scp 'yow-cgts4-lx:/localdisk/loadbuild/jenkins/WRCP_21.12_Build/last_build_with_test_patches/test_patches/*{A,B,C}.patch' "${PATCHES_DIR}/" + fi +} + +# Executed at start of all tests +oneTimeSetUp() { + th_info oneTimeSetUp + if [ -d "${BUILDDIR}" ]; then + th_debug "Cleaning ${BUILDDIR}" + rm -rf "${BUILDDIR}" + fi + mkdir -p "${BUILDDIR}" || fail "Failed to create ${BUILDDIR}" + _create_fake_images + _fetch_iso_and_patches +} + +# Executed at start of each tests +setUp() { + th_info setUp +} + +oneTimeTearDown() { + th_info oneTimeTearDown + if [ -z "${KEEP_ARTIFACTS}" ]; then + if [ -n "${BUILDDIR}" ] && [ -d "${BUILDDIR}" ]; then + th_debug "Cleaning ${BUILDDIR}" + rm -rf "${BUILDDIR}" + fi + fi +} + +# Executed at completion of each tests +tearDown() { + th_info tearDown + if [ -n "${MNTDIR}" ] && [ -d "${MNTDIR}" ]; then + th_info "tearDown: unmounting ${MNTDIR}" + sudo umount "${MNTDIR}" 2>/dev/null || th_warn "umount failed for ${MNTDIR}" + fi + if [ -f "${OUTPUT_ISO}" ]; then + sudo rm "${OUTPUT_ISO}" + fi +} + +create_ks_addon_file() { +cat <<EOF > "${BUILDDIR}/ks-addon.cfg" +ilog "Executing ks-addon.cfg" +EOF +} + +validate_generated_iso() { + local syslinux_boot=1 + local grub_boot=graphical + local param= + local syslinux_timeout=300 + local grub_timeout=30 + local ks_addon= + while [ $# -gt 0 ] ; do + case "${1:-""}" in + --syslinux-boot) + shift + syslinux_boot=$1 + ;; + --grub-boot) + shift + grub_boot=$1 + ;; + --param) + shift + param=$1 + ;; + --syslinux-timeout) + shift + syslinux_timeout=$1 + ;; + --grub-timeout) + shift + grub_timeout=$1 + ;; + --ks-addon) + shift + ks_addon=ks-addon.cfg + ;; + *) + echo "Invalid expected value '$1'" + exit 1 + ;; + esac + shift + done + + # Mount the ISO + [ -f "${OUTPUT_ISO}" ] || fail "${OUTPUT_ISO} does not exist" + MNTDIR=${BUILDDIR}/mnt + [ -d "${MNTDIR}" ] || mkdir "${MNTDIR}" || fail "mkdir failed" + sudo mount "${OUTPUT_ISO}" "${MNTDIR}" || fail "Failed to mount ${OUTPUT_ISO}" + + # Check that images are included: + th_info "Image check: validating images are in ISO" + for image in "${IMAGES_DIR}"/*.tar.gz; do + [ "$(find "${MNTDIR}"/opt/platform-backup -name "$(basename "${image}")" | wc -l)" -eq 1 ] \ + || fail "Missing expected image ${image}" + th_info "Found expected image: ${image}" + done + th_info "Image check passed" + + # Check that patches are included: + th_info "Patch check: validating patches are in ISO" + for patch in "${PATCHES_DIR}"/*.patch; do + [ "$(find "${MNTDIR}"/opt/platform-backup -name "$(basename "${patch}")" | wc -l)" -eq 1 ] \ + || fail "Missing expected patch ${patch}" + th_info "Found expected patch: ${patch}" + done + th_info "Patch check passed" + + local syslinux_cfg=${MNTDIR}/syslinux.cfg + local grub_cfg=${MNTDIR}/EFI/BOOT/grub.cfg + + if [ -n "${syslinux_boot}" ]; then + grep -q -i "default ${syslinux_boot}" "${syslinux_cfg}" \ + || fail "Incorrect syslinux boot: ${syslinux_boot} in ${syslinux_cfg}" + fi + if [ -n "${syslinux_timeout}" ]; then + grep -q "timeout ${syslinux_timeout}" "${syslinux_cfg}" \ + || fail "Incorrect syslinux timeout: (expected ${syslinux_timeout}) in ${syslinux_cfg}" + fi + if [ -n "${grub_boot}" ]; then + grep 'default=' "${grub_cfg}" | grep -q "${grub_boot}" \ + || fail "Incorrect grub boot (expected ${grub_boot}) in EFI/BOOT/grub.cfg" + fi + if [ -n "${grub_timeout}" ]; then + grep -q "timeout=${grub_timeout}" "${grub_cfg}" \ + || fail "Incorrect grub timeout: ${grub_timeout} in EFI/BOOT/grub.cfg" + fi + if [ -n "${param}" ]; then + # There should be two boot entries containing param (graphical + serial) + [ "$(grep -c "${param}" "${syslinux_cfg}")" -eq 2 ] \ + || fail "Incorrect param value (expected ${param}) in ${syslinux_cfg}" + [ "$(grep -c "${param}" "${grub_cfg}")" -eq 2 ] \ + || fail "Incorrect param value (expected ${param}) in EFI/BOOT/grub.cfg" + fi + if [ -n "${ks_addon}" ]; then + [ -f "${MNTDIR}/${ks_addon}" ] || fail "Expected ks-addon ${ks_addon} not found" + fi +} + +test_generate_prestaged_iso_1() { + th_info "Running test_generate_prestaged_iso_1" + ( # subshell + cd "${TARGET_SCRIPTDIR}" + local images="" + local image + for image in "${IMAGES_DIR}"/*.tar.gz; do + images="${images} --image ${image}" + done + local patches="" + local patch + for patch in "${PATCHES_DIR}"/*.patch; do + patches="${patches} --patch ${patch}" + done + # shellcheck disable=2086 + sudo ./gen-prestaged-iso-centos.sh --input "${ISOFILE}" \ + --output "${OUTPUT_ISO}" \ + ${images} ${patches} + ) || fail "gen-prestaged-iso-centos.sh failed" + + th_info "Generated ${OUTPUT_ISO}" + + if [ -n "${KEEP_ARTIFACTS}" ]; then + th_info "Preserving ISO in ${SCRIPTDIR}/generated_centos_1.iso" + cp "${OUTPUT_ISO}" "${SCRIPTDIR}"/generated_centos_1.iso + fi + + validate_generated_iso --syslinux-boot 1 --grub-boot graphical \ + --syslinux-timeout 300 --grub-timeout 30 +} + +test_generate_prestaged_iso_2() { + th_info "Running test_generate_prestaged_iso_2" + ( # subshell + cd "${TARGET_SCRIPTDIR}" + local images="" + local image + for image in "${IMAGES_DIR}"/*.tar.gz; do + if [ -z "${images}" ]; then + images="--image ${image}" + else + images="${images},${image}" + fi + done + local patches="" + local patch + for patch in "${PATCHES_DIR}"/*.patch; do + if [ -z "${patches}" ]; then + patches="--patch ${patch}" + else + patches="${patches},${patch}" + fi + done + create_ks_addon_file + + # shellcheck disable=2086 + sudo ./gen-prestaged-iso-centos.sh --input "${ISOFILE}" \ + --output "${OUTPUT_ISO}" \ + --addon "${BUILDDIR}/ks-addon.cfg" \ + --default-boot 0 \ + --timeout 90 \ + --force-install \ + --param "param1=1,param2=2" \ + ${images} ${patches} + ) || fail "gen-prestaged-iso-centos.sh failed" + + th_info "Generated ${OUTPUT_ISO}" + + if [ -n "${KEEP_ARTIFACTS}" ]; then + th_info "Preserving ISO in ${SCRIPTDIR}/generated_centos_2.iso" + cp "${OUTPUT_ISO}" "${SCRIPTDIR}"/generated_centos_2.iso + fi + + validate_generated_iso --syslinux-boot 0 --grub-boot serial \ + --syslinux-timeout 900 --grub-timeout 90 \ + --param "param1=1 param2=2" --ks-addon ks-addon.cfg +} + +# shellcheck disable=SC2154 +trap 'rc=$?; echo "Caught abnormal signal rc=$rc"; exit $rc' 2 3 15 + +th_info "Running shunit2" + +# Load and run shunit2. +# shellcheck disable=SC2034 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. "${TH_SHUNIT}" diff --git a/utilities/platform-util/scripts/test/gen-prestaged-iso-test.sh b/utilities/platform-util/scripts/test/gen-prestaged-iso-test.sh new file mode 100755 index 00000000..4e8f7421 --- /dev/null +++ b/utilities/platform-util/scripts/test/gen-prestaged-iso-test.sh @@ -0,0 +1,302 @@ +#!/bin/bash +# vim:ft=sh:sts=4:sw=4 + +# This is a shunit2 test file. +# See https://github.com/kward/shunit2 +# Run the tests by executing this script. + +# shellcheck disable=SC2016 +# shellcheck disable=SC1090 +# shellcheck disable=SC1091 + +NAME=gen-prestaged-iso-test + +# shellcheck disable=SC2155,SC2034 +readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") +# shellcheck disable=SC2155,SC2034 +readonly TARGET_SCRIPTDIR=$(readlink -m "${SCRIPTDIR}/..") + +INPUT_DIR="${SCRIPTDIR}"/input/debian +IMAGES_DIR="${INPUT_DIR}/images" +PATCHES_DIR="${INPUT_DIR}/patches" +ISOFILE=${ISOFILE:-$INPUT_DIR/starlingx-intel-x86-64-cd.iso} +#ISOFILE=/localdisk/designer/kmacleod/dc-libvirt/isofiles/wrcp-22.12-release/starlingx-intel-x86-64-cd.iso + +# source the script under test +. "${TARGET_SCRIPTDIR}"/stx-iso-utils.sh +. "${SCRIPTDIR}/shunit2_helper.sh" + +KEEP_ARTIFACTS=${KEEP_ARTIFACTS:-} +BUILDDIR=${SCRIPTDIR}/output/${NAME} +OUTPUT_ISO=${BUILDDIR}/generated.iso + +_create_fake_image() { + local imagename=$1 + local targetdir=$2 + if hash docker 2>/dev/null; then + echo "Creating fake image ${imagename} using docker" + tar cv --files-from /dev/null | docker import - "${imagename}:latest" + docker save -o "${targetdir}/${imagename}.tar.gz" "${imagename}:latest" + docker rmi "${imagename}:latest" + else + echo "Creating fake empty image ${imagename}" + touch "${targetdir}/${imagename}.tar.gz" + fi +} + +_create_fake_images() { + if [ ! -d "${IMAGES_DIR}" ]; then + echo "Creating fake images" + mkdir -p "${IMAGES_DIR}" || fail "mkdir failed" + local image + for image in image1 image2 image3; do + _create_fake_image "${image}" "${IMAGES_DIR}" + done + fi +} + +_fetch_iso_and_patches() { + # Fetch Debian ISO and test patches from yow-wrcp-lx + [ -d "${INPUT_DIR}" ] || mkdir -p "${INPUT_DIR}" || fail "mkdir failed" + if [ ! -f "${ISOFILE}" ]; then + echo "Fetching ISO" + scp 'yow-wrcp-lx:/localdisk/loadbuild/jenkins/wrcp-master-debian/latest_build/export/outputs/iso/starlingx-intel-x86-64-2*-cd.*' "${INPUT_DIR}/" || fail "scp iso failed" + ln -s "${INPUT_DIR}"/starlingx-intel-x86-64-2*-cd.iso "${INPUT_DIR}"/starlingx-intel-x86-64-cd.iso + ln -s "${INPUT_DIR}"/starlingx-intel-x86-64-2*-cd.sig "${INPUT_DIR}"/starlingx-intel-x86-64-cd.sig + fi + if [ ! -d "${PATCHES_DIR}" ]; then + echo "Fetching patches" + mkdir -p "${PATCHES_DIR}" || fail "mkdir failed" + scp 'yow-wrcp-lx:/localdisk/loadbuild/jenkins/wrcp-master-debian/latest_build/test_patches/*{NRR_INSVC,RR_ALL_NODES,_RR_ALL_NODES_REQUIRES}.patch' "${INPUT_DIR}/patches/" || fail "scp iso failed" + fi +} + +# Executed at start of all tests +oneTimeSetUp() { + th_info oneTimeSetUp + if [ -d "${BUILDDIR}" ]; then + th_debug "Cleaning ${BUILDDIR}" + rm -rf "${BUILDDIR}" + fi + mkdir -p "${BUILDDIR}" || fail "Failed to create ${BUILDDIR}" + _create_fake_images + _fetch_iso_and_patches +} + +# Executed at start of each tests +setUp() { + th_info setUp +} + +oneTimeTearDown() { + th_info oneTimeTearDown + if [ -z "${KEEP_ARTIFACTS}" ]; then + if [ -n "${BUILDDIR}" ] && [ -d "${BUILDDIR}" ]; then + th_debug "Cleaning ${BUILDDIR}" + rm -rf "${BUILDDIR}" + fi + fi +} + +# Executed at completion of each tests +tearDown() { + th_info tearDown + if [ -n "${MNTDIR}" ] && [ -d "${MNTDIR}" ]; then + th_info "tearDown: unmounting ${MNTDIR}" + sudo umount "${MNTDIR}" 2>/dev/null || th_warn "umount failed for ${MNTDIR}" + fi + if [ -f "${OUTPUT_ISO}" ]; then + sudo rm "${OUTPUT_ISO}" + fi +} + +create_ks_addon_file() { +cat <<EOF > "${BUILDDIR}/ks-addon.cfg" +ilog "Executing ks-addon.cfg" +EOF +} + +validate_generated_iso() { + local syslinux_boot=1 + local grub_boot=graphical + local param= + local syslinux_timeout=300 + local grub_timeout=30 + local ks_addon= + while [ $# -gt 0 ] ; do + case "${1:-""}" in + --syslinux-boot) + shift + syslinux_boot=$1 + ;; + --grub-boot) + shift + grub_boot=$1 + ;; + --param) + shift + param=$1 + ;; + --syslinux-timeout) + shift + syslinux_timeout=$1 + ;; + --grub-timeout) + shift + grub_timeout=$1 + ;; + --ks-addon) + shift + ks_addon=ks-addon.cfg + ;; + *) + echo "Invalid expected value '$1'" + exit 1 + ;; + esac + shift + done + + # Mount the ISO + [ -f "${OUTPUT_ISO}" ] || fail "${OUTPUT_ISO} does not exist" + MNTDIR=${BUILDDIR}/mnt + [ -d "${MNTDIR}" ] || mkdir "${MNTDIR}" || fail "mkdir failed" + sudo mount "${OUTPUT_ISO}" "${MNTDIR}" || fail "Failed to mount ${OUTPUT_ISO}" + + # Check that images are included: + th_info "Image check: validating images are in ISO" + for image in "${IMAGES_DIR}"/*.tar.gz; do + [ "$(find "${MNTDIR}"/opt/platform-backup -name "$(basename "${image}")" | wc -l)" -eq 1 ] \ + || fail "Missing expected image ${image}" + th_info "Found expected image: ${image}" + done + th_info "Image check passed" + + # Check that patches are included: + th_info "Patch check: validating patches are in ISO" + for patch in "${PATCHES_DIR}"/*.patch; do + [ "$(find "${MNTDIR}"/opt/platform-backup -name "$(basename "${patch}")" | wc -l)" -eq 1 ] \ + || fail "Missing expected patch ${patch}" + th_info "Found expected patch: ${patch}" + done + th_info "Patch check passed" + + local syslinux_cfg=${MNTDIR}/isolinux/isolinux.cfg + local grub_cfg=${MNTDIR}/EFI/BOOT/grub.cfg + + if [ -n "${syslinux_boot}" ]; then + grep -q -i "default ${syslinux_boot}" "${syslinux_cfg}" \ + || fail "Incorrect syslinux boot: ${syslinux_boot} in ${syslinux_cfg}" + fi + if [ -n "${syslinux_timeout}" ]; then + grep -q "timeout ${syslinux_timeout}" "${syslinux_cfg}" \ + || fail "Incorrect syslinux timeout: (expected ${syslinux_timeout}) in ${syslinux_cfg}" + fi + if [ -n "${grub_boot}" ]; then + grep 'default=' "${grub_cfg}" | grep -q "${grub_boot}" \ + || fail "Incorrect grub boot (expected ${grub_boot}) in EFI/BOOT/grub.cfg" + fi + if [ -n "${grub_timeout}" ]; then + grep -q "timeout=${grub_timeout}" "${grub_cfg}" \ + || fail "Incorrect grub timeout: ${grub_timeout} in EFI/BOOT/grub.cfg" + fi + if [ -n "${param}" ]; then + # There should be two boot entries containing param (graphical + serial) + [ "$(grep -c "${param}" "${syslinux_cfg}")" -eq 2 ] \ + || fail "Incorrect param value (expected ${param}) in ${syslinux_cfg}" + [ "$(grep -c "${param}" "${grub_cfg}")" -eq 2 ] \ + || fail "Incorrect param value (expected ${param}) in EFI/BOOT/grub.cfg" + fi + if [ -n "${ks_addon}" ]; then + [ -f "${MNTDIR}/${ks_addon}" ] || fail "Expected ks-addon ${ks_addon} not found" + fi +} + +test_generate_prestaged_iso_1() { + th_info "Running test_generate_prestaged_iso_1" + ( + cd "${TARGET_SCRIPTDIR}" + local images="" + local image + for image in "${IMAGES_DIR}"/*.tar.gz; do + images="${images} --image ${image}" + done + local patches="" + local patch + for patch in "${PATCHES_DIR}"/*.patch; do + patches="${patches} --patch ${patch}" + done + # shellcheck disable=2086 + sudo ./gen-prestaged-iso.sh --input "${ISOFILE}" \ + --output "${OUTPUT_ISO}" \ + ${images} ${patches} + ) || fail "gen-prestaged-iso.sh failed" + + th_info "Generated ${OUTPUT_ISO}" + + if [ -n "${KEEP_ARTIFACTS}" ]; then + th_info "Preserving ISO in ${SCRIPTDIR}/generated_debian_1.iso" + cp "${OUTPUT_ISO}" "${SCRIPTDIR}"/generated_debian_debian_1.iso + fi + + validate_generated_iso --syslinux-boot 1 --grub-boot graphical \ + --syslinux-timeout 300 --grub-timeout 30 +} + +test_generate_prestaged_iso_2() { + th_info "Running test_generate_prestaged_iso_2" + ( # subshell + cd "${TARGET_SCRIPTDIR}" + local images="" + local image + for image in "${IMAGES_DIR}"/*.tar.gz; do + if [ -z "${images}" ]; then + images="--image ${image}" + else + images="${images},${image}" + fi + done + local patches="" + local patch + for patch in "${PATCHES_DIR}"/*.patch; do + if [ -z "${patches}" ]; then + patches="--patch ${patch}" + else + patches="${patches},${patch}" + fi + done + create_ks_addon_file + + # shellcheck disable=2086 + sudo ./gen-prestaged-iso.sh --input "${ISOFILE}" \ + --output "${OUTPUT_ISO}" \ + --addon "${BUILDDIR}/ks-addon.cfg" \ + --default-boot 0 \ + --timeout 90 \ + --force-install \ + --param "param1=1,param2=2" \ + ${images} ${patches} \ + ) || fail "gen-prestaged-iso.sh failed" + + th_info "Generated ${OUTPUT_ISO}" + + if [ -n "${KEEP_ARTIFACTS}" ]; then + th_info "Preserving ISO in ${SCRIPTDIR}/generated_debian_2.iso" + cp "${OUTPUT_ISO}" "${SCRIPTDIR}"/generated_debian_2.iso + fi + + validate_generated_iso --syslinux-boot 0 --grub-boot serial \ + --syslinux-timeout 900 --grub-timeout 90 \ + --param "param1=1 param2=2" --ks-addon ks-addon.cfg +} + + +# shellcheck disable=SC2154 +trap 'rc=$?; echo "Caught abnormal signal rc=$rc"; exit $rc' 2 3 15 + +th_info "Running shunit2" + +# Load and run shunit2. +# shellcheck disable=SC2034 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. "${TH_SHUNIT}" diff --git a/utilities/platform-util/scripts/test/run-tests.sh b/utilities/platform-util/scripts/test/run-tests.sh new file mode 100755 index 00000000..0cdde852 --- /dev/null +++ b/utilities/platform-util/scripts/test/run-tests.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +echo "Running all unit tests" + +# shellcheck disable=SC2155,SC2034 +readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") +cd "${SCRIPTDIR}" || { echo "cd failed"; exit 1; } + +TESTS="./gen-prestaged-iso-test.sh ./gen-prestaged-iso-test.sh" + +log_progress() { echo -e "$(tput setaf 2)$*$(tput sgr0)"; } +log_error() { echo -e "$(tput setaf 1)ERROR: $*$(tput sgr0)"; } + +declare -i rc +for testscript in ${TESTS}; do + log_progress "--------------------------------------------------------------------------------" + log_progress "Executing ${testscript}" + ${testscript} + rc=$? + if [ $rc -ne 0 ]; then + log_error "failure in ${testscript}, rc=${rc}" + exit $rc + fi +done diff --git a/utilities/platform-util/scripts/test/shunit2_helper.sh b/utilities/platform-util/scripts/test/shunit2_helper.sh new file mode 100644 index 00000000..17a35383 --- /dev/null +++ b/utilities/platform-util/scripts/test/shunit2_helper.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# ----------------------------------------------------------------------------- +# Helper stuff taken from https://github.com/kward/shunit2/blob/master/shunit2_test_helpers + +# Note: the last release, 2.1.8 is buggy/not working. Checkout the git repo and use that instead. +SHUNIT_DIR="${SCRIPTDIR:-.}/shunit2" +if [ ! -d "${SHUNIT_DIR}" ]; then + ( # subprocess + cd "$SCRIPTDIR" + git clone "https://github.com/kward/shunit2.git" || { echo "ERROR: failed to clone shunit2"; exit 1; } + ) +fi +# Path to shunit2 library. Can be overridden by setting SHUNIT_INC. +TH_SHUNIT=${SHUNIT_DIR}/shunit2 export TH_SHUNIT + +set -e # Exit immediately if a simple command exits with a non-zero status. +set -u # Treat unset variables as an error when performing parameter expansion. + +# Set shwordsplit for zsh. +[ -n "${ZSH_VERSION:-}" ] && setopt shwordsplit + +# +# Constants. +# + +# Configure debugging. Set the DEBUG environment variable to any +# non-empty value to enable debug output, or TRACE to enable trace +# output. +TRACE=${TRACE:+'th_trace '} +[ -n "${TRACE}" ] && DEBUG=1 +[ -z "${TRACE}" ] && TRACE=':' + +DEBUG=${DEBUG:+'th_debug '} +[ -z "${DEBUG}" ] && DEBUG=':' + +# +# Functions. +# + +# Logging functions. +th_trace() { echo "test:TRACE $*" >&2; } +th_debug() { echo "test:DEBUG $*" >&2; } +th_info() { echo "test:INFO $*" >&2; } +th_warn() { echo "test:WARN $*" >&2; } +th_error() { echo "test:ERROR $*" >&2; } +th_fatal() { echo "test:FATAL $*" >&2; } + +# Output subtest name. +th_subtest() { echo " $*" >&2; } + + +# +# Bind in our own non-exiting functions. These overwrite the functions from stx-iso-utils.sh: +# +function elog { + echo "$(date "+%F %H-%M-%S") Error: $*" >&2 + return 1 +} + +function check_rc_die { + local -i rc=$1; shift + if [ $rc -ne 0 ]; then + echo "$(date "+%F %H-%M-%S") Error: $*" >&2 + return $rc + fi +} +