mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
0e08b9d256
* feat: ship per-test JUnit results to the Elasticsearch sink Every e2e workflow already converts its test log to JUnit XML and renders it into the job summary, then discards it with the artifact. mulga-ci-runs records which suite failed; nothing records which test failed, so root-causing a failure means reading raw Actions logs across historical runs. The junit-to-es action bulk-indexes one doc per leaf test case into mulga-ci-test-results: name, subtest, status, duration, failure text, run id, cell, suite, branch and sha. Parent t.Run wrappers are dropped so a failing subtest counts once, matching the job summary renderer. The doc id is deterministic, so a replayed post overwrites rather than double-counting. Called from every workflow that produces JUnit XML, and tolerated on failure: an unreachable sink must not turn a green test run red. * feat: classify unparsed JUnit cases as unknown, not error go-junit-report emits <error message="No test result found"> when a === RUN line has no matching result line, which means the log it was fed was truncated or filtered. Recording that as a failure would put 78 phantom failures per green bare-metal run into the pass rate.
171 lines
6.2 KiB
YAML
171 lines
6.2 KiB
YAML
name: E2E Bare-Metal
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
workflow_call:
|
|
inputs:
|
|
ref:
|
|
description: "spinifex ref to checkout/test (default: triggering ref)"
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
otel-attrs:
|
|
description: "OTEL_RESOURCE_ATTRIBUTES value for this run, e.g. ci.run_id=123,ci.cell=20 (caller-specific, so it's an input rather than a workflow-level default)"
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
|
|
permissions:
|
|
contents: read
|
|
statuses: write
|
|
|
|
env:
|
|
GIT_BRANCH: ${{ github.ref_name }}
|
|
# Same central ES/APM sink as the other four e2e workflows. Bare-metal had
|
|
# no default here before — this cell's telemetry never reached the sink at
|
|
# all (not just missing ci.cell), see bm-bootstrap.sh's telemetry.env step.
|
|
SPINIFEX_OTLP_ENDPOINT: ${{ vars.SPINIFEX_OTLP_ENDPOINT || 'http://192.168.1.13:8200' }}
|
|
|
|
jobs:
|
|
e2e_baremetal:
|
|
name: Bare-Metal Single-Node E2E
|
|
runs-on: [ self-hosted, ci-baremetal ]
|
|
timeout-minutes: 120
|
|
concurrency:
|
|
group: e2e-baremetal-cluster
|
|
cancel-in-progress: false
|
|
env:
|
|
BM_ISO_CACHE: /var/cache/spinifex-iso-builder
|
|
ARTIFACT_DIR: /tmp/spinifex-e2e-nightly-cell-20-${{ github.run_id }}
|
|
# Caller-supplied (workflow_call input); empty for a manual
|
|
# workflow_dispatch run, which bm-bootstrap.sh treats as "no attrs".
|
|
SPINIFEX_OTEL_ATTRS: ${{ inputs.otel-attrs }}
|
|
steps:
|
|
- name: Clean Workspace
|
|
run: |
|
|
sudo umount -R -l "${BM_ISO_CACHE}/rootfs" 2>/dev/null || true
|
|
if [ ! -c /dev/null ]; then sudo rm -f /dev/null; sudo mknod -m 666 /dev/null c 1 3; fi
|
|
sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/*
|
|
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
|
|
- uses: actions/create-github-app-token@v3
|
|
id: app-token
|
|
with:
|
|
client-id: ${{ secrets.APP_ID }}
|
|
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
repositories: mulga
|
|
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
repository: mulgadc/mulga
|
|
token: ${{ steps.app-token.outputs.token }}
|
|
sparse-checkout: |
|
|
scripts/iso-builder
|
|
scripts/baremetal
|
|
path: mulga
|
|
|
|
- name: Install SSH Key
|
|
run: |
|
|
echo "${{ secrets.BM_SSH_PRIVATE_KEY }}" > "${RUNNER_TEMP}/bm-ci"
|
|
chmod 0600 "${RUNNER_TEMP}/bm-ci"
|
|
ssh-keygen -y -f "${RUNNER_TEMP}/bm-ci" > "${RUNNER_TEMP}/bm-ci.pub"
|
|
|
|
- name: Build Artifacts
|
|
run: |
|
|
mulga/scripts/baremetal/bm-build-artifacts.sh \
|
|
"$GITHUB_WORKSPACE" \
|
|
"${RUNNER_TEMP}/spinifex-tests.tar" \
|
|
"${RUNNER_TEMP}/bm-bins"
|
|
|
|
- name: Build PXE Artifacts
|
|
run: |
|
|
sudo make -C mulga/scripts/iso-builder \
|
|
build-rootfs build-squashfs build-initramfs \
|
|
CACHE="${BM_ISO_CACHE}" \
|
|
SPINIFEX_BIN="${RUNNER_TEMP}/bm-bins/spx" \
|
|
INSTALLER_BIN="${RUNNER_TEMP}/bm-bins/spinifex-installer" \
|
|
SPINIFEX_SRC="${GITHUB_WORKSPACE}" \
|
|
VIPERBLOCK_SRC="${RUNNER_TEMP}/bm-bins/viperblock" \
|
|
VERSION="$(cd "$GITHUB_WORKSPACE" && git describe --tags --always 2>/dev/null || echo dev)"
|
|
|
|
- name: Write ePDU credentials
|
|
run: |
|
|
install -m 0600 /dev/null "${RUNNER_TEMP}/epdu.env"
|
|
cat >> "${RUNNER_TEMP}/epdu.env" <<EOF
|
|
EPDU_HOST=192.168.1.63
|
|
EPDU_SNMP_USER=ci-bmpower
|
|
EPDU_SNMP_SEC_LEVEL=authPriv
|
|
EPDU_SNMP_AUTH_PROTO=MD5
|
|
EPDU_SNMP_AUTH_PASS=${{ secrets.BM_EPDU_AUTH_PASS }}
|
|
EPDU_SNMP_PRIV_PROTO=DES
|
|
EPDU_SNMP_PRIV_PASS=${{ secrets.BM_EPDU_PRIV_PASS }}
|
|
EOF
|
|
|
|
- name: Cache VM images
|
|
run: |
|
|
UBUNTU_IMG="${BM_ISO_CACHE}/ubuntu-26.04-cloudimg-amd64.img"
|
|
if [ ! -f "$UBUNTU_IMG" ]; then
|
|
echo "Downloading Ubuntu 26.04 cloud image..."
|
|
sudo curl -fsSL -o "$UBUNTU_IMG" \
|
|
"https://cloud-images.ubuntu.com/resolute/current/resolute-server-cloudimg-amd64.img"
|
|
echo " Downloaded $(du -h "$UBUNTU_IMG" | cut -f1)"
|
|
else
|
|
echo "Ubuntu 26.04 cloud image cached ($(du -h "$UBUNTU_IMG" | cut -f1))"
|
|
fi
|
|
|
|
- name: Install go-junit-report on runner
|
|
run: go install github.com/jstemmer/go-junit-report/v2@latest
|
|
|
|
- name: Run Bare-Metal E2E
|
|
env:
|
|
BM_CI_PASSWORD: ${{ secrets.BM_CI_PASSWORD }}
|
|
BM_SSH_KEY: ${{ runner.temp }}/bm-ci
|
|
BM_EPDU_ENV: ${{ runner.temp }}/epdu.env
|
|
BM_UBUNTU_IMAGE: ${{ env.BM_ISO_CACHE }}/ubuntu-26.04-cloudimg-amd64.img
|
|
SPX_EXTERNAL_POOL: ${{ env.SPINIFEX_CI_POOL }}
|
|
run: |
|
|
mkdir -p "${ARTIFACT_DIR}"
|
|
set -o pipefail
|
|
sudo -E mulga/scripts/baremetal/run-baremetal-e2e.sh \
|
|
--vmlinuz "${BM_ISO_CACHE}/vmlinuz" \
|
|
--initrd "${BM_ISO_CACHE}/initrd.img" \
|
|
--squashfs "${BM_ISO_CACHE}/spinifex.squashfs" \
|
|
--tests-tar "${RUNNER_TEMP}/spinifex-tests.tar" \
|
|
2>&1 | tee "${ARTIFACT_DIR}/test-baremetal.log"
|
|
|
|
- name: Generate JUnit report
|
|
if: always()
|
|
run: |
|
|
LOG="${ARTIFACT_DIR}/test-baremetal.log"
|
|
[ -f "$LOG" ] || exit 0
|
|
go-junit-report -in "$LOG" -out "${ARTIFACT_DIR}/junit-baremetal.xml" || true
|
|
|
|
# Tolerated on failure: the sink being unreachable must not turn a
|
|
# green test run red.
|
|
- name: Ship test results to Elasticsearch
|
|
if: always()
|
|
continue-on-error: true
|
|
uses: ./.github/actions/junit-to-es
|
|
with:
|
|
junit-glob: ${{ env.ARTIFACT_DIR }}/junit-*.xml
|
|
cell: "20"
|
|
|
|
- name: Analyze e2e failures
|
|
if: always()
|
|
uses: ./.github/actions/e2e-analyze
|
|
with:
|
|
junit-glob: ${{ env.ARTIFACT_DIR }}/junit-*.xml
|
|
log-dir: ${{ env.ARTIFACT_DIR }}
|
|
title: "E2E failure analysis (cell 20)"
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: e2e-artifacts-cell-20-${{ github.run_id }}
|
|
path: ${{ env.ARTIFACT_DIR }}
|
|
if-no-files-found: warn
|
|
retention-days: 14
|