Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- "docker-github-actions"
- "release/v1"
- "beta"
- "bugfix/e2e"
tags:
- "*"
jobs:
Expand Down
83 changes: 83 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: E2E Test (reusable)

on:
workflow_call:
inputs:
image-artifact-name:
required: true
type: string
distro-name:
required: true
type: string
docker-context:
required: false
type: string
default: 'testing/'
timeout-minutes:
required: false
type: number
default: 45
poll-interval:
required: false
type: number
default: 5
max-poll-iterations:
required: false
type: number
default: 360

jobs:
e2e-test:
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout-minutes }}
steps:
- uses: actions/checkout@v4

- name: Download image from build
uses: actions/download-artifact@v4
with:
name: ${{ inputs.image-artifact-name }}
path: image/

- name: Build test Docker image
run: DOCKER_BUILDKIT=0 docker build -t e2e-test ${{ inputs.docker-context }}

- name: Start E2E test container
run: |
mkdir -p artifacts
IMG=$(find image/ -name '*.img' | head -1)
docker run -d --name e2e-test \
-v "$PWD/artifacts:/output" \
-v "$(realpath $IMG):/input/image.img:ro" \
-e ARTIFACTS_DIR=/output \
-e DISTRO_NAME="${{ inputs.distro-name }}" \
e2e-test

- name: Wait for tests to complete
run: |
for i in $(seq 1 ${{ inputs.max-poll-iterations }}); do
[ -f artifacts/exit-code ] && break
sleep ${{ inputs.poll-interval }}
done
if [ ! -f artifacts/exit-code ]; then
echo "ERROR: Tests did not complete within timeout"
docker logs e2e-test 2>&1 | tail -80
exit 1
fi
echo "Tests finished with exit code: $(cat artifacts/exit-code)"
cat artifacts/test-results.txt 2>/dev/null || true

- name: Collect logs and stop container
if: always()
run: |
docker logs e2e-test > artifacts/container.log 2>&1 || true
docker stop e2e-test 2>/dev/null || true

- name: Check test result
run: exit "$(cat artifacts/exit-code 2>/dev/null || echo 1)"

- uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-test-results
path: artifacts/
12 changes: 11 additions & 1 deletion src/distro_testing/scripts/prepare-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ write /etc/fstab "proc /proc proc defaults 0 0\n/dev/vda1 /boot/firmware vfat de
-rm /etc/systemd/system/multi-user.target.wants/userconfig.service
-rm /usr/lib/systemd/system/userconfig.service

# userconfig.service is what normally drops /etc/sudoers.d/010_pi-nopasswd.
# Since we remove it, seed the file ourselves so test scripts (post-boot hooks,
# apt-get installs, etc.) can use passwordless sudo over a non-TTY SSH session.
mkdir-p /etc/sudoers.d
write /etc/sudoers.d/010_pi-nopasswd "pi ALL=(ALL) NOPASSWD: ALL\n"
chmod 0440 /etc/sudoers.d/010_pi-nopasswd

mkdir-p /etc/ssh/sshd_config.d
write /etc/ssh/sshd_config.d/99-qemu-test.conf "PasswordAuthentication yes\nPermitRootLogin yes\nKbdInteractiveAuthentication yes\n"

Expand Down Expand Up @@ -71,17 +78,20 @@ chmod 0644 /etc/ssh/ssh_host_ecdsa_key.pub
chmod 0644 /etc/ssh/ssh_host_ed25519_key.pub

download /etc/shadow /tmp/shadow.bak
download /etc/passwd /tmp/passwd.bak

umount /
GFEOF

echo 'Setting pi user password...'
echo 'Setting pi user password and shell...'
sed -i "s|^pi:[^:]*:|pi:${PIPASS}:|" /tmp/shadow.bak
sed -i 's|^pi:\(.*\):/usr/sbin/nologin$|pi:\1:/bin/bash|' /tmp/passwd.bak

guestfish -a "$OUTPUT_IMAGE" <<GFEOF2
run
mount /dev/sda2 /
upload /tmp/shadow.bak /etc/shadow
upload /tmp/passwd.bak /etc/passwd
umount /
GFEOF2

Expand Down
26 changes: 26 additions & 0 deletions src/distro_testing/scripts/ssh-helpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
# Shared SSH helpers for E2E test scripts.
# Source this file: source /test/scripts/ssh-helpers.sh
#
# Set E2E_SSH_HOST, E2E_SSH_PORT, E2E_SSH_USER, E2E_SSH_PASS before sourcing
# to override defaults. Test scripts typically do:
# export E2E_SSH_HOST="${1:-localhost}"
# export E2E_SSH_PORT="${2:-2222}"
# source /test/scripts/ssh-helpers.sh

E2E_SSH_HOST="${E2E_SSH_HOST:-localhost}"
E2E_SSH_PORT="${E2E_SSH_PORT:-2222}"
E2E_SSH_USER="${E2E_SSH_USER:-pi}"
E2E_SSH_PASS="${E2E_SSH_PASS:-raspberry}"

SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o PreferredAuthentications=password -o PubkeyAuthentication=no \
-o ConnectTimeout=10 -o LogLevel=ERROR"

ssh_cmd() {
sshpass -p "$E2E_SSH_PASS" ssh $SSH_OPTS -p "$E2E_SSH_PORT" "${E2E_SSH_USER}@${E2E_SSH_HOST}" "$@"
}

scp_cmd() {
sshpass -p "$E2E_SSH_PASS" scp $SSH_OPTS -P "$E2E_SSH_PORT" "$@"
}
11 changes: 5 additions & 6 deletions src/distro_testing/tests/test_boot.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#!/bin/bash
set -e

HOST="${1:-localhost}"
PORT="${2:-2222}"
USER="pi"
PASS="raspberry"
export E2E_SSH_HOST="${1:-localhost}"
export E2E_SSH_PORT="${2:-2222}"

SSH_CMD="sshpass -p $PASS ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=password -o PubkeyAuthentication=no -o LogLevel=ERROR -p $PORT ${USER}@${HOST}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$(dirname "$SCRIPT_DIR")/scripts/ssh-helpers.sh"

echo "Test: SSH login and run 'echo hello world'"

OUTPUT=$($SSH_CMD 'echo hello world' 2>/dev/null)
OUTPUT=$(ssh_cmd 'echo hello world' 2>/dev/null)

if [ "$OUTPUT" = "hello world" ]; then
echo " Output: '$OUTPUT'"
Expand Down
Loading