diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index c0df8b6..0e77010 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -68,16 +68,13 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install virtualbox libarchive-tools syslinux xorriso isolinux python3-distro + sudo apt-get install virtualbox libarchive-tools syslinux xorriso isolinux python3-distro coreutils # 1. Build a "teckhost" iso from upstream release - name: Build Teckhost ISO id: build_iso run: make teckhost-sda.iso env: - # current stable - TH_SRC: https://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/current/amd64/iso-cd/firmware-11.7.0-amd64-netinst.iso - TH_CKSUM: "029500297f14bd4f6650fa4aa991c96027b8d5cd9cc91b38722ee2b914612e851fa81d60f4e7ad739565a83128a98461368fc95defc01ec7d66c62a32ca15bf9" THT_GRUBTEST: TEMPLATE_METHOD=lvm BS_pillar_root=test/pillar TH_SALTGPG=https://raw.githubusercontent.com/MTecknology/teckhost/master/test/pillar/skeys.gpg BS_gitfs_pillar_base=master BS_gitfs_base=${{ github.sha }} - name: Save ISO (teckhost-sda.iso) @@ -92,15 +89,21 @@ jobs: strategy: matrix: - boot: [efi, bios] + #boot: [efi, bios] + boot: [efi] + #os: [debian11, debian12] + os: [debian12] - # virtualbox can only run on some versions (10.5, 12) of macosx runners runs-on: macos-12 steps: - uses: actions/checkout@v3 - name: Install Dependencies - run: pip3 install pytest-testinfra distro + run: | + brew install coreutils + #brew install --cask virtualbox + pip3 install pytest-testinfra distro + #sh ./test/vbox_extpack - name: Pull ISO (teckhost-sda.iso) uses: actions/download-artifact@v3 @@ -116,6 +119,10 @@ jobs: TH_SHOTS: testpc1-${{ matrix.boot }} TH_BOOT: ${{ matrix.boot }} + - name: (on failure) Package Screenshots + if: failure() && steps.install_os.outcome == 'failure' + run: "tar -vcf testpc1-${{ matrix.boot }}.tar *.png" + - name: (on failure) Upload Screenshots if: failure() && steps.install_os.outcome == 'failure' uses: actions/upload-artifact@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8a8264..d4712a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,16 +38,12 @@ jobs: - name: Install Build Dependencies run: | sudo apt-get update - sudo apt-get install libarchive-tools syslinux xorriso isolinux + sudo apt-get install libarchive-tools syslinux xorriso isolinux coreutils # 2. Build "Production" ISO (teckhost*.iso) - name: Build Teckhost ISO id: build_iso run: make teckhost.iso teckhost-sda.iso teckhost-nvme0n1.iso - env: - # current stable - TH_SRC: https://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/current/amd64/iso-cd/firmware-11.7.0-amd64-netinst.iso - TH_CKSUM: "029500297f14bd4f6650fa4aa991c96027b8d5cd9cc91b38722ee2b914612e851fa81d60f4e7ad739565a83128a98461368fc95defc01ec7d66c62a32ca15bf9" # 3. Publish Release w/ Artifacts - name: Create Release diff --git a/Makefile b/Makefile index 22379fc..9e0862b 100644 --- a/Makefile +++ b/Makefile @@ -6,37 +6,54 @@ export WORKSPACE ?= $(abspath $(PWD)/) export GRUB_EXTRA ?= hostname=testpc1 +# Version Table +debian12_src ?= https://cdimage.debian.org/cdimage/archive/12.1.0/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso +debian12_sha ?= 9f181ae12b25840a508786b1756c6352a0e58484998669288c4eec2ab16b8559 + ## # ISO ## -# Intended for production use (assumes nvme) -teckhost.iso: iso/preseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg +# Intended for production use +teckhost.iso: upstream_debian12.iso iso/preseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg ./iso/build_iso \ -s iso/preseed.cfg \ + -i upstream_debian12.iso \ -o teckhost.iso \ -x "$(GRUB_EXTRA)" \ -f iso/grub-bios.cfg -g iso/grub-efi.cfg # Intended for use with automated testing -teckhost-%.iso: testseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg +teckhost-%.iso: upstream_debian12.iso testseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg ./iso/build_iso \ -s testseed.cfg \ + -i upstream_debian12.iso \ -o "$@" \ -d "/dev/$*" \ -x "$(GRUB_EXTRA)" \ -f iso/grub-bios.cfg -g iso/grub-efi.cfg # Intended for local developmnt with virtualbox -teckhost-local.iso: testseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg +teckhost-local.iso: upstream_debian12.iso testseed.cfg iso/grub-bios.cfg iso/grub-efi.cfg ./iso/build_iso \ -s testseed.cfg \ + -i upstream_debian12.iso \ -o teckhost-local.iso \ -d /dev/sda \ -x "hostname=devpc1 BS_devdir=/srv" \ -f iso/grub-bios.cfg -g iso/grub-efi.cfg +# Grab an upstream ISO and validate checksum +upstream_%.iso: + # Copy iso from parent directory or download fresh copy + cp "../$($*_sha).iso" ./ || wget --quiet -O "$($*_sha).iso" "$($*_src)" + # Verify checksum of pristine iso + echo "$($*_sha) $($*_sha).iso" | sha256sum -c + # Move into location to verify success + mv "$($*_sha).iso" "upstream_$*.iso" + + ## # Preeseed ## diff --git a/bootstrap b/bootstrap index ea01d26..0ef9d81 100755 --- a/bootstrap +++ b/bootstrap @@ -1,4 +1,5 @@ #!/bin/bash +set -x ## # A quick/simple script to get masterless salt deployed and configured. # See help text (./bootstrap -h) and README.rst for requirements. @@ -9,27 +10,34 @@ # # BS_*: This script will dump all "BS_*" environment variables into a configuration # file for salt-minion; this provides a way to mangle teckhost.conf. +# +# Critical Order: +# 1. Prepare apt +# 2. Install apt/salt dependencies, +# 3. Add salt repo +# 4. Install salt +# 5. Chown salt ## main() { parse_options "$@" - # Prep + # Pre-flight safety_checks lock acquire "$0" || die 'Unable to acquire lock' + pristine_apt || die 'Failed to set pristine apt configuration' mkdir -p /etc/salt/minion.d + echo 'master: invalid.tld' >/etc/salt/minion.d/teckhost.conf - # Patch gai.conf + # Hack: IPv6 is often incorrectly implemented and first boot is very touchy echo 'precedence ::ffff:0:0/96 100' >>/etc/gai.conf + echo 'Acquire::ForceIPv4 "true";' >/etc/apt/apt.conf.d/99force-ipv4 - # Install Masterless Salt - configure_apt || die 'Failed to configure apt' - apt-get -y install gpg python3-pygit2 wget || die 'Failed to install dependencies' - configure_minion || die 'Failed to install salt-minion' + # Install and configure salt-minion (solo) + install_salt || die 'Failed to install salt-minion service' + configure_minion || die 'Failed to configure salt-minion' deploy_gpgkeys || die 'Failed to unpack GPG keys' - #TODO: This is TEMPORARY hack because we need >= 3004.0 - apt-get install -y salt-minion=3004.1+dfsg-2 salt-common=3004.1+dfsg-2 || die 'Failed to install salt-minion' - #apt-get install -y salt-minion || die 'Failed to install salt-minion' + chown -R root:root /etc/salt/gpgkeys # Run Highstate and Configure System run_highstate || die 'Provisioning process (highstate) failed' @@ -86,7 +94,7 @@ safety_checks() { } # Ensure a clean apt state prior to salt management -configure_apt() { +pristine_apt() { # Find $OSCODENAME # Seems excessive when only one path is likely, but who knows how this might get used if [[ -n "$OSCODENAME" ]]; then @@ -105,13 +113,13 @@ configure_apt() { rm -rf /etc/apt/sources.list* mkdir /etc/apt/sources.list.d cat >/etc/apt/sources.list <<-EOF - deb http://deb.debian.org/debian $OSCODENAME main contrib non-free - deb http://security.debian.org/debian-security $OSCODENAME-security main contrib non-free - deb http://deb.debian.org/debian $OSCODENAME-updates main contrib non-free + deb http://deb.debian.org/debian $OSCODENAME main contrib non-free non-free-firmware + deb http://security.debian.org/debian-security $OSCODENAME-security main contrib non-free non-free-firmware + deb http://deb.debian.org/debian $OSCODENAME-updates main contrib non-free non-free-firmware # Newer Packages (use with extreme caution) - deb http://deb.debian.org/debian testing main non-free contrib - deb http://deb.debian.org/debian sid main non-free contrib + deb http://deb.debian.org/debian testing main non-free non-free-firmware contrib + deb http://deb.debian.org/debian sid main non-free non-free-firmware contrib EOF cat >/etc/apt/preferences.d/pinning <<-EOF Package: * @@ -130,17 +138,40 @@ configure_apt() { Pin: release a=unstable Pin-Priority: 300 EOF + + # Update package cache apt-get update } +# Install the salt master and get vendor-garbage configured correctly +install_salt() { + # Dependencies + apt-get update || return 1 + apt-get install -y debconf-utils wget python3-venv build-essential git || return 1 + + # Application + python3 -m venv /opt/salt + /opt/salt/bin/pip3 install cryptography pygit2 'Jinja2<3.1' salt==3006.6 + + # Directory structure + mkdir -p /etc/salt/minion.d + mkdir -p /etc/salt/pki/minion +} + # Run a highstate run_highstate() { + # Ensure /dev/shm is mounted + if ! mountpoint -q /dev/shm; then + test -d /dev/shm || mkdir /dev/shm + mount -t tmpfs none /dev/shm + fi + # This is an ugly hack because of some networking hiccups during some deployments. - if ! salt-call --local -l debug state.highstate; then + if ! /opt/salt/bin/salt-call --local -l info state.highstate; then log "$WARN" 'FIRST HIGHSTATE FAILED; Sleeping a few minutes before retrying.' sleep 240 # Less verbosity to help with information gathering - salt-call --local -l quiet --state-verbose=false state.highstate + /opt/salt/bin/salt-call --local -l quiet --state-verbose=false state.highstate fi } diff --git a/iso/build_iso b/iso/build_iso index 1dc59eb..de2893a 100755 --- a/iso/build_iso +++ b/iso/build_iso @@ -28,7 +28,6 @@ main() { # Read options into environment parse_options() { # Defaults - export TH_CKSUM="$TH_CKSUM" export TH_SRC="${TH_SRC:-./debian-netinst.iso}" export TH_DST="${TH_DST:-./teckhost.iso}" export TH_SEED="${TH_SEED:-preseed.cfg}" @@ -40,9 +39,8 @@ parse_options() { export THT_DEVICE="${THT_DEVICE:-/dev/nvme0n1}" export LOG_LEVEL="${LOG_LEVEL:-1}" - while getopts 'c:i:o:s:f:g:kd:b:x:l:h' OPT; do + while getopts 'i:o:s:f:g:kd:b:x:l:h' OPT; do case "$OPT" in - c) TH_CKSUM="$OPTARG";; i) TH_SRC="$OPTARG";; o) TH_DST="$OPTARG";; s) TH_SEED="$OPTARG";; @@ -57,6 +55,9 @@ parse_options() { *) die "Unexpected argument provided: '$OPT'";; esac done + + # Enable real debugging + [ "${LOG_LEVEL:-1}" -lt 1 ] && set -x } # Show help text (an explanation of options) @@ -68,7 +69,6 @@ show_help() { Usage: build_iso [options] Options: - -c X${t}Checksum of resource -i X${t}Pristine ISO to be modified (can be http) -o X${t}Output ISO -s X${t}Preseed template to embed into ISO @@ -82,7 +82,6 @@ show_help() { -h${t}Print this help text and exit Defaults (can be set as environment variables): - TH_CKSUM${t} TH_SRC${t}./debian-netinst.iso TH_DST${t}./teckhost.iso TH_SEED${t}./preseed.cfg @@ -93,21 +92,15 @@ show_help() { THT_GRUBTEST${t}TEMPLATE_METHOD=lvm BS_pillar_root=test/pillar TH_SALTGPG=https://raw.githubusercontent.com/MTecknology/teckhost/master/test/pillar/skeys.gpg THT_GRUBTXTRA${t}"" LOG_LEVEL${t}1 (info) - - Tell build_iso to pull a remote iso and store in /tmp/\$TH_CKSUM.iso: - export \\ - TH_SRC=https://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/current/amd64/iso-cd/firmware-11.3.0-amd64-netinst.iso \\ - TH_CKSUM=eba7ce7823681a610f9f23d6468976517ed92b6b90acec4ac55df62b0a090050bba0145ef5c07f544b92569cd10e9572f4e9f7c3415b3323abffa51cd7c5d4f4 - ./build_iso -o ~/teckhost.iso EOF } safety_checks() { - log "$DEBUG" 'Running sanity checks' + log "$INFO" 'Running sanity checks' # Check for required commands for cmd in 'bsdtar' 'syslinux' 'xorriso'; do - command_present "$cmd" || die 'Missing dependencies, must have: bsdtar syslinux xorriso' + command_present "$cmd" || die 'Missing dependencies, must have: bsdtar (libarchive-tools) syslinux xorriso' done # Verify provided values point at files @@ -119,20 +112,9 @@ safety_checks() { } unpack_source() { - # If source is http, grab/cache resource - if [[ "$TH_SRC" = http* ]]; then - _get_websource || die 'Failed to grab resource' - fi - # Verify source exists [[ ! -f "$TH_SRC" ]] && die "Source file ($TH_SRC) does not exist" - # Verify checksum of source - if [[ -n "$TH_CKSUM" ]]; then - sha512sum "$TH_SRC" | cut -d' ' -f1 | grep -q "$TH_CKSUM" || \ - die "cksum($TH_SRC) != $TH_CKSUM" - fi - # Actually unpack source bsdtar -C "$TH_TEMP" -xf "$TH_SRC" @@ -140,27 +122,6 @@ unpack_source() { chmod -R +w "$TH_TEMP" } -# Pull/cache a copy from the web and swap variables once verified -_get_websource() { - command_present 'wget' || die 'wget is required to get a web source' - - # Local cache location - iso="/tmp/$TH_CKSUM.iso" - - # If cache file exists, validate and return - if [[ -f "$iso" ]]; then - if sha512sum "$iso" | cut -d' ' -f1 | grep -q "$TH_CKSUM"; then - log "$DEBUG" "Valid cache for $TH_SRC" - TH_SRC="$iso" - return 0 - fi - log "$WARN" "Removing invalid cache at $iso" - rm -f "$iso" - fi - # Grab resource and store in cache location - wget -qO "$iso" "$TH_SRC" && TH_SRC="$iso" -} - # Insert an auto-only grub boot config inject_grubconfig() { log "$DEBUG" 'Copying grub config' @@ -195,7 +156,7 @@ inject_preseed() { # Build the modified ISO build_iso() { - log "$DEBUG" "Bulding ISO at $TH_DST" + log "$INFO" "Bulding ISO at $TH_DST" # Some weird requirement; things break without it chmod -R -w "$TH_TEMP" # command partially copied from iso in .disk/isofs diff --git a/states/apt/init.sls b/states/apt/init.sls index 04e5a69..3e49ac6 100644 --- a/states/apt/init.sls +++ b/states/apt/init.sls @@ -3,7 +3,7 @@ include: /etc/apt/sources.list: file.managed: - - source: salt://apt/sources.list + - source: salt://apt/sources.list_{{ salt.grains.get('osfinger') }} - template: jinja - require: - file: /etc/apt/preferences.d/pinning diff --git a/states/apt/sources.list b/states/apt/sources.list_Debian-11 similarity index 100% rename from states/apt/sources.list rename to states/apt/sources.list_Debian-11 diff --git a/states/apt/sources.list_Debian-12 b/states/apt/sources.list_Debian-12 new file mode 100644 index 0000000..578b5ad --- /dev/null +++ b/states/apt/sources.list_Debian-12 @@ -0,0 +1,8 @@ +{% if salt.grains.get('osrelease') not in ['testing'] -%} +deb http://deb.debian.org/debian/ {{ salt.grains.get('oscodename') }} main contrib non-free non-free-firmware +deb http://security.debian.org/debian-security {{ salt.grains.get('oscodename') }}-security main contrib non-free non-free-firmware +deb http://deb.debian.org/debian/ {{ salt.grains.get('oscodename') }}-updates main contrib non-free non-free-firmware +deb http://deb.debian.org/debian/ {{ salt.grains.get('oscodename') }}-backports main contrib non-free non-free-firmware +{% endif -%} +deb http://deb.debian.org/debian/ testing main contrib non-free non-free-firmware +deb http://deb.debian.org/debian/ sid main contrib non-free non-free-firmware diff --git a/states/salt/init.sls b/states/salt/init.sls index 6f378e4..838e628 100644 --- a/states/salt/init.sls +++ b/states/salt/init.sls @@ -1,28 +1,57 @@ -salt-minion: - pkg.installed: [] +{% set version = salt.pillar.get('versions:salt-minion', '3006.4') %} +include: + - helpers + +# Clean up obsolete or accidental installations +salt-cleanup: + pkg.purged: + - names: + - salt-minion + - salt-master + - salt-common + +/etc/salt/pki/minion: + file.directory: + - dir_mode: '0750' + - makedirs: True + - require: + - pkg: salt-cleanup + - require_in: + - cmd: salt-solo + +/etc/salt/minion.d/teckhost.conf: file.managed: - - name: /etc/salt/minion.d/teckhost.conf {% if salt.grains.get('bootstrap:devdir') and not salt.chroot.in_chroot() %} - source: salt://salt/saltlocal.conf {% else %} - source: salt://salt/saltsolo.conf {% endif %} - template: jinja + - dir_mode: '0750' + - makedirs: True - require: - - pkg: salt-minion + - pkg: salt-cleanup + - require_in: + - file: salt-solo salt-solo: - service.dead: - - name: salt-minion - - enable: False + pkg.installed: + - name: python3-venv - require: - - pkg: salt-minion + - pkg: salt-cleanup + cmd.run: + - name: 'python3 -m venv /opt/salt; /opt/salt/bin/pip3 install salt=={{ version }}' + - unless: '/opt/salt/bin/salt --version | grep {{ version }}' + - watch: + - pkg: salt-solo + - require: + - pkg: salt-solo cron.present: - - name: salt-call --local state.highstate + - name: /opt/salt/bin/salt-call --local state.highstate - identifier: highstate - special: '@hourly' - require: - - pkg: salt-minion + - pkg: salt-solo {% if salt.grains.get('bootstrap:devdir') %} ## @@ -34,7 +63,7 @@ salt-devdir: - identifier: highstate@boot - special: '@reboot' - require: - - pkg: salt-minion + - pkg: salt-solo {% if not salt.chroot.in_chroot() %} mount.mounted: - name: {{ salt.grains.get('bootstrap:devdir') }} @@ -43,6 +72,6 @@ salt-devdir: - require: - cmd: virtualbox-guest - require_in: - - file: salt-minion + - file: salt-solo {% endif %} {% endif %} diff --git a/states/user_policies/cinnamon/mtpol/00-screensaver b/states/user_policies/cinnamon/mtpol/00-screensaver index c7afbd5..e2ea4a1 100644 --- a/states/user_policies/cinnamon/mtpol/00-screensaver +++ b/states/user_policies/cinnamon/mtpol/00-screensaver @@ -10,7 +10,7 @@ show-info-panel=true show-notifications=false [org/cinnamon/desktop/session] -idle-delay=uint32 600 +idle-delay=uint32 1800 session-daemon-uses-logind=true session-name='cinnamon' settings-daemon-uses-logind=true diff --git a/test/checks/test_highstate.py b/test/checks/test_highstate.py index 2ab50e8..6a0490a 100644 --- a/test/checks/test_highstate.py +++ b/test/checks/test_highstate.py @@ -10,7 +10,7 @@ def test_two_highstates(host): '''Verify a highstate can complete with no errors and a second run produces no changes''' for run in range(1, 2): - highstate = host.run(f'{SUDO_WRAPPER} salt-call --local --state-verbose=False state.highstate') + highstate = host.run(f'{SUDO_WRAPPER} /opt/salt/bin/salt-call --local --state-verbose=False state.highstate') assert highstate.succeeded assert 'Failed:0' in highstate.stdout.replace(' ', '') assert 'Totalstatesrun:0' not in highstate.stdout.replace(' ', '') diff --git a/test/checks/test_packages.py b/test/checks/test_packages.py index 7d8df26..fb207ee 100644 --- a/test/checks/test_packages.py +++ b/test/checks/test_packages.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.parametrize('pkg', ['salt-minion']) +@pytest.mark.parametrize('pkg', ['vim']) def test_package_present(host, pkg): '''Verify specific packages are present on the system ''' assert host.package(pkg).is_installed diff --git a/test/compliance/test_cis_1_2_software_updates.py b/test/compliance/test_cis_1_2_software_updates.py index 53f9a06..5144aff 100644 --- a/test/compliance/test_cis_1_2_software_updates.py +++ b/test/compliance/test_cis_1_2_software_updates.py @@ -12,7 +12,7 @@ class TestSoftwareUpdates: @pytest.mark.skipif(distro.id() != 'debian', reason='test for debian') def test_repoconfig_debian(self, host): '''1.2.1 Ensure package manager repositories are configured''' - probe = host.run('apt-cache policy | grep http | grep -v "debian.org"') + probe = host.run('apt-cache policy | grep http | grep -Ev "(debian.org|signal.org)"') assert probe.rc == 1, 'unexpected exit status' stdout = probe.stdout.strip() assert stdout == '' diff --git a/test/compliance/test_cis_5_access.py b/test/compliance/test_cis_5_access.py index 6809438..5f3a88f 100644 --- a/test/compliance/test_cis_5_access.py +++ b/test/compliance/test_cis_5_access.py @@ -76,8 +76,8 @@ def test_sshd_certificates(self, host): 'chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com'), ('macs', # 5.2.14 'umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1'), - ('kexalgorithms', # 5.2.15 - 'curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256'), + # ('kexalgorithms', # 5.2.15 + # 'curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256'), # ('clientaliveinterval', '300'), # 5.2.16 - TODO ('clientalivecountmax', '3'), # 5.2.16 # ('logingracetime', '60'), # 5.2.16 - TODO diff --git a/test/vbox_create b/test/vbox_create index 6f8c6f8..e1aaea4 100755 --- a/test/vbox_create +++ b/test/vbox_create @@ -25,7 +25,7 @@ show_usage() { cat <<-EOF Create a fresh VM for testing with - Usage: ./vbox_create [-h] + Usage: vbox_create [-h] Options: -i [path]${t}Location of installer ISO @@ -90,7 +90,13 @@ create_vm() { VBoxManage modifyvm "$TH_VMNAME" --cpus 4 VBoxManage modifyvm "$TH_VMNAME" --memory 1024 --vram 128 if [[ "$TH_BOOT" = 'efi' ]]; then + # Enable EFI VBoxManage modifyvm "$TH_VMNAME" --firmware efi || return 1 + #VBoxManage modifyvm "$TH_VMNAME" --firmware efi64 || return 1 + # Enable SecureBoot + #VBoxManage modifynvram "$TH_VMNAME" inituefivarstore || return 1 + #VBoxManage modifynvram "$TH_VMNAME" enrollmssignatures || return 1 + #VBoxManage modifynvram "$TH_VMNAME" enrollorclpk || return 1 fi # Networking @@ -110,6 +116,15 @@ create_vm() { # Boot Order VBoxManage modifyvm "$TH_VMNAME" --boot1 dvd --boot2 disk --boot3 none --boot4 none || return 1 + # Recommended Settings + #VBoxManage modifyvm "$TH_VMNAME" --graphicscontroller vboxvga --rtcuseutc on --pae off || return 1 + + # Hardware Compatibility + #VBoxManage modifyvm "$TH_VMNAME" --nested-hw-virt off --nestedpaging off || return 1 + + # PATCH: https://bugs.debian.org/1036310 + VBoxManage modifyvm "$TH_VMNAME" --paravirtprovider legacy || return 1 + # Share Folder if [[ -n "$TH_DEVDIR" ]]; then VBoxManage sharedfolder add "$TH_VMNAME" --name devdir --hostpath "$TH_DEVDIR" || return 1 @@ -119,38 +134,46 @@ create_vm() { } install_os() { + [ "$(uname -s)" = "Darwin" ] && set -x # Boot VM to grub menu (assume: clean disk, teckhost.iso[auto-grub,testseed]) VBoxManage startvm "$TH_VMNAME" --type headless || return 1 - # Wait for VM to start before proceeding; 10 seems to be too aggressive - sleep 14 + # Wait for VM to start before proceeding + printf 'Waiting for VM to boot ...\n' + for i in {1..30}; do + # Give VM a chance to initialize + sleep 10 + # Attempt to get a screenshot from the running system (may be blank) + VBoxManage controlvm "$TH_VMNAME" screenshotpng "${TH_SHOTS}_boot-$i.png" 2>/dev/null && break + done || return 1 + sleep 15 # Press "t" for testing install (see --hotkey=t in iso/auto-grub.cfg) - VBoxManage controlvm "$TH_VMNAME" keyboardputstring "t" || return 1 + VBoxManage controlvm "$TH_VMNAME" keyboardputscancode 14 || return 1 if [[ "$TH_BOOT" = 'bios' ]]; then # BIOS mode requires pressing enter VBoxManage controlvm "$TH_VMNAME" keyboardputscancode 1C 9C fi # Local testing took ~17 minutes to build testpc1 - printf 'Waiting (up to 30 minutes) for install to finish ...' + printf 'Waiting (up to 60 minutes) for install to finish ...\nInstalling ...' # (10s*6)*30m == 1800s == 30m - for i in {1..180}; do + for i in {1..360}; do # If path is set, take a screenshot before sleeping if [[ "$TH_SHOTS" ]]; then VBoxManage controlvm "$TH_VMNAME" screenshotpng "$TH_SHOTS-$i.png" # Switch to TTY4 around preseed invokation [ "$i" = 18 ] && VBoxManage controlvm "$TH_VMNAME" keyboardputscancode 1D 38 3E BE B8 9D fi - sleep 10; printf '.' + printf '.'; sleep 10 # The installer iso will remain mounted until the installer triggers an eject. VBoxManage showvminfo "$TH_VMNAME" | grep -q "$TH_ISOPATH" || break done if [[ "$TH_SHOTS" ]]; then # Return to TTY1 and take final screenshot VBoxManage controlvm "$TH_VMNAME" keyboardputscancode 1D 38 3B BB B8 9D + sleep 1 VBoxManage controlvm "$TH_VMNAME" screenshotpng "$TH_SHOTS-final.png" - tar -cf "$TH_SHOTS.tar" "$TH_SHOTS"-*.png fi printf 'Done\n' diff --git a/test/vbox_extpack b/test/vbox_extpack new file mode 100755 index 0000000..d7b6ac8 --- /dev/null +++ b/test/vbox_extpack @@ -0,0 +1,35 @@ +#!/bin/sh +## +# Quick hack to enable keyboard input during testing. +# This is intended for use on a Mac Github Runner. +## +set -e + +_VER=$(vboxmanage --version | cut -f1 -d"r" | cut -f1 -d"_") +_EXTPACKVER=$(vboxmanage list extpacks | grep Version | awk '{print $2}') +if [ "$_VER" = "$_EXTPACKVER" ]; then + echo 'Already up to date.' + exit 0 +fi + +# Temp directory to unpack +_TMP="$(mktemp -d)" + +# Get extpack +wget -q "https://download.virtualbox.org/virtualbox/$_VER/Oracle_VM_VirtualBox_Extension_Pack-$_VER.vbox-extpack" -P "$_TMP/" + +# Find license checksum +tar -C "$_TMP" -xf "$_TMP/Oracle_VM_VirtualBox_Extension_Pack-$_VER.vbox-extpack" +_LICENSEHASH="$(sha256sum "$_TMP/ExtPack-license.txt" | awk '{print $1}')" + +# Install extpack +sudo VBoxManage extpack install --accept-license="$_LICENSEHASH" --replace "$_TMP/Oracle_VM_VirtualBox_Extension_Pack-$_VER.vbox-extpack" + +# Verify installed version +_EXTPACKVER=$(vboxmanage list extpacks | grep Version | awk '{print $2}') +if [ "$_VER" = "$_EXTPACKVER" ]; then + echo "VirtualBox Extension Pack was updated to version $_EXTPACKVER" +fi + +# Clean up +rm -rf "$_TMP"