Skip to content
Draft
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
234 changes: 234 additions & 0 deletions .github/workflows/lvh-integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
name: QEMU Integration Tests

on:
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.head_ref || github.run_id }}-qemu
cancel-in-progress: true

env:
VM_DIR: /tmp/fact-vm
SSH_PORT: '2222'

jobs:
build:
name: Build fact image
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
with:
submodules: true
fetch-depth: 0
persist-credentials: false

- name: Build image
run: |
FACT_REGISTRY=localhost/fact make image

- name: Export image
run: |
docker save -o /tmp/fact-image.tar "$(FACT_REGISTRY=localhost/fact make image-name)"

- name: Upload image artifact
uses: actions/upload-artifact@v4
with:
name: fact-image
path: /tmp/fact-image.tar
retention-days: 1

integration-tests:
name: Integration tests (${{ matrix.name }})
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- name: centos-9-stream
image_url: https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2
checksum_url: https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2.SHA256SUM
cloud_init: hack/cloud-init/centos.yml
- name: centos-10-stream
image_url: https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-latest.x86_64.qcow2
checksum_url: https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-latest.x86_64.qcow2.SHA256SUM
cloud_init: hack/cloud-init/centos.yml
- name: fedora-44
image_url: https://download.fedoraproject.org/pub/fedora/linux/releases/44/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-44-1.7.x86_64.qcow2
checksum_url: https://download.fedoraproject.org/pub/fedora/linux/releases/44/Cloud/x86_64/images/Fedora-Cloud-44-1.7-x86_64-CHECKSUM
cloud_init: hack/cloud-init/fedora.yml
- name: rhcos-4.16
# RHCOS has no direct download page,see the "Resolve and
# download RHCOS image" step.
rhcos_stream: '4.16'
ignition: hack/ignition/rhcos.json
ssh_user: core
container_runtime: podman
steps:
- name: Checkout fact
uses: actions/checkout@v7
with:
submodules: true
fetch-depth: 0
persist-credentials: false

- name: Install QEMU
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq --no-install-recommends \
qemu-system-x86 qemu-utils genisoimage virtiofsd jq
[ -w /dev/kvm ] || sudo chmod 666 /dev/kvm

- name: Download VM base image
if: matrix.rhcos_stream == ''
run: |
mkdir -p "${VM_DIR}"
image_name="$(basename "${{ matrix.image_url }}")"
curl -fSL --progress-bar -o "${VM_DIR}/${image_name}" "${{ matrix.image_url }}"

# Checksum files may list multiple images (Ubuntu SHA256SUMS) or
# use either GNU ("hash filename") or BSD ("SHA256 (filename) =
# hash") tagged format. sha256sum -c auto-detects both, so we
# only need to filter the file down to our image's line.
curl -fsSL "${{ matrix.checksum_url }}" | grep -F "${image_name}" > "${VM_DIR}/${image_name}.sum"
(cd "${VM_DIR}" && sha256sum -c "${image_name}.sum")
rm -f "${VM_DIR}/${image_name}.sum"

mv "${VM_DIR}/${image_name}" "${VM_DIR}/base.qcow2"

- name: Resolve and download RHCOS image
if: matrix.rhcos_stream != ''
run: |
mkdir -p "${VM_DIR}"

# RHCOS has no stable download URL; the current build for a
# given OpenShift release stream is published as JSON metadata
# by the openshift/installer project, freely downloadable with
# no Red Hat subscription required.
stream_json="$(curl -fsSL "https://raw.githubusercontent.com/openshift/installer/release-${{ matrix.rhcos_stream }}/data/data/coreos/rhcos.json")"
qemu_artifact="$(echo "${stream_json}" | jq -c '.architectures.x86_64.artifacts.qemu.formats."qcow2.gz".disk')"
location="$(echo "${qemu_artifact}" | jq -r '.location')"
gz_sha256="$(echo "${qemu_artifact}" | jq -r '.sha256')"
raw_sha256="$(echo "${qemu_artifact}" | jq -r '."uncompressed-sha256"')"

curl -fSL --progress-bar -o "${VM_DIR}/rhcos.qcow2.gz" "${location}"
echo "${gz_sha256} ${VM_DIR}/rhcos.qcow2.gz" | sha256sum -c -

gunzip -f "${VM_DIR}/rhcos.qcow2.gz"
echo "${raw_sha256} ${VM_DIR}/rhcos.qcow2" | sha256sum -c -

mv "${VM_DIR}/rhcos.qcow2" "${VM_DIR}/base.qcow2"

- name: Download fact image artifact
uses: actions/download-artifact@v4
with:
name: fact-image

- name: Start VM
run: |
args=(
--image "${VM_DIR}/base.qcow2"
--vm-dir "${VM_DIR}"
--ssh-port "${SSH_PORT}"
--ssh-user "${{ matrix.ssh_user || 'root' }}"
--runtime "${{ matrix.container_runtime || 'docker' }}"
--host-mount "${{ github.workspace }}"
)
if [ -n "${{ matrix.cloud_init }}" ]; then
args+=(--cloud-init "${{ matrix.cloud_init }}")
else
args+=(--ignition "${{ matrix.ignition }}")
fi
hack/qemu-vm.sh start "${args[@]}"

- name: Load fact image into VM
run: |
hack/qemu-vm.sh ssh --vm-dir "${VM_DIR}" --ssh-port "${SSH_PORT}" \
--ssh-user "${{ matrix.ssh_user || 'root' }}" -- \
"${{ matrix.container_runtime || 'docker' }}" load -i /mnt/host/fact-image.tar

- name: Setup test environment in VM
run: |
# Generate gRPC stubs directly (matching tests/Makefile's
# grpc-gen target) instead of via "make", so the VM doesn't
# need a make binary installed.
hack/qemu-vm.sh ssh --vm-dir "${VM_DIR}" --ssh-port "${SSH_PORT}" \
--ssh-user "${{ matrix.ssh_user || 'root' }}" -- \
bash -c '
set -euo pipefail
cd /mnt/host/tests
python3 -m venv .venv
source .venv/bin/activate
pip install -q -r requirements.txt
python3 -m grpc_tools.protoc \
-I../third_party/stackrox/proto \
--python_out=. \
--pyi_out=. \
--grpc_python_out=. \
../third_party/stackrox/proto/internalapi/sensor/collector.proto \
../third_party/stackrox/proto/internalapi/sensor/sfa.proto \
../third_party/stackrox/proto/internalapi/sensor/sfa_iservice.proto
'

- name: Run integration tests
run: |
# NOTE: --no-local-builds is intentionally omitted. The editor,
# fedora, and self-deleter test containers are normally pulled
# from quay.io/rhacs-eng/qa-multi-arch, which is a private
# registry that fork PR runs have no credentials for. Without
# --no-local-builds, tests/containers.py::pull_or_build() falls
# back to building those images locally from their Containerfiles
# on a pull error.
FACT_IMAGE="$(FACT_REGISTRY=localhost/fact make --no-print-directory image-name)"

# podman exposes a Docker-API-compatible socket that the
# docker Python SDK (used throughout tests/) can talk to
# directly via DOCKER_HOST -- no separate Docker daemon needed.
CONTAINER_RUNTIME="${{ matrix.container_runtime || 'docker' }}"
DOCKER_HOST_ENV=""
if [ "${CONTAINER_RUNTIME}" = "podman" ]; then
DOCKER_HOST_ENV="export DOCKER_HOST=unix:///run/podman/podman.sock"
fi

hack/qemu-vm.sh ssh --vm-dir "${VM_DIR}" --ssh-port "${SSH_PORT}" \
--ssh-user "${{ matrix.ssh_user || 'root' }}" -- \
bash -c "
set -euo pipefail
${DOCKER_HOST_ENV}
mkdir -p /tmp/fact-tmp
cd /mnt/host/tests
source .venv/bin/activate
pytest \
--image='${FACT_IMAGE}' \
--output=grpc \
--tmp-dir=/tmp/fact-tmp \
--junit-xml=results.xml \
--tb=short
"

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: qemu-test-results-${{ matrix.name }}
path: |
tests/results.xml
tests/logs/
if-no-files-found: ignore

- name: Test summary
uses: test-summary/action@v2
if: always()
with:
paths: tests/results.xml

- name: Dump VM console on failure
if: failure()
run: cat "${VM_DIR}/console.log" 2>/dev/null || true

- name: Stop VM
if: always()
run: hack/qemu-vm.sh stop --vm-dir "${VM_DIR}"
19 changes: 19 additions & 0 deletions hack/cloud-init/centos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#cloud-config
# Cloud-init user-data for CentOS Stream 10 (and RHEL 10).
# Used by hack/qemu-vm.sh — __SSH_PUBKEY__ is replaced at boot time.

users:
- name: root
lock_passwd: false
ssh_authorized_keys:
- __SSH_PUBKEY__

ssh_pwauth: false

runcmd:
- dnf -y install dnf-plugins-core
- dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- systemctl enable --now docker
- dnf -y install python3 python3-pip python3-devel gcc
- touch /var/lib/cloud/instance/boot-finished-user
19 changes: 19 additions & 0 deletions hack/cloud-init/fedora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#cloud-config
# Cloud-init user-data for Fedora (41+, dnf5-based).
# Used by hack/qemu-vm.sh — __SSH_PUBKEY__ is replaced at boot time.

users:
- name: root
lock_passwd: false
ssh_authorized_keys:
- __SSH_PUBKEY__

ssh_pwauth: false

runcmd:
- dnf -y install dnf5-plugins
- dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo
- dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- systemctl enable --now docker
- dnf -y install python3 python3-pip python3-devel gcc
- touch /var/lib/cloud/instance/boot-finished-user
38 changes: 38 additions & 0 deletions hack/cloud-init/ubuntu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#cloud-config
# Cloud-init user-data for Ubuntu (24.04+).
# Used by hack/qemu-vm.sh — __SSH_PUBKEY__ is replaced at boot time.
#
# NOTE: Ubuntu uses AppArmor by default; the LSM list must include it
# alongside bpf. The grubby tool is not available — use a GRUB
# drop-in config file instead (see runcmd below for why a plain
# /etc/default/grub edit does not work).

users:
- name: root
lock_passwd: false
ssh_authorized_keys:
- __SSH_PUBKEY__

ssh_pwauth: false

runcmd:
# Ubuntu cloud images ship /etc/default/grub.d/50-cloudimg-settings.cfg,
# which sets GRUB_CMDLINE_LINUX_DEFAULT and is sourced by grub-mkconfig
# AFTER /etc/default/grub, clobbering any edits made there. Write our
# own drop-in with a higher-sorting filename so it wins instead.
- |
cat > /etc/default/grub.d/99-fact-lsm.cfg <<'EOF'
GRUB_CMDLINE_LINUX_DEFAULT="console=tty1 console=ttyS0 lsm=lockdown,capability,yama,apparmor,bpf"
EOF
- update-grub
- apt-get update -qq
- apt-get install -y -qq ca-certificates curl
- install -m 0755 -d /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
- chmod a+r /etc/apt/keyrings/docker.asc
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
- apt-get update -qq
- apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- systemctl enable --now docker
- apt-get install -y -qq python3-venv python3-dev gcc
- touch /var/lib/cloud/instance/boot-finished-user
23 changes: 23 additions & 0 deletions hack/ignition/rhcos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"ignition": {
"version": "3.4.0"
},
"passwd": {
"users": [
{
"name": "core",
"sshAuthorizedKeys": [
"__SSH_PUBKEY__"
]
}
]
},
"systemd": {
"units": [
{
"name": "podman.socket",
"enabled": true
}
]
}
}
Loading
Loading