Skip to content
Closed
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
33 changes: 28 additions & 5 deletions .github/workflows/gha-kvm-system-rdte.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ on:
- wow-sidecar
- garm
- garm-provider-g2
- garm-provider-g3

permissions:
contents: read
Expand Down Expand Up @@ -107,7 +108,7 @@ jobs:
scripts/gha_kvm_proxmox_rdte.sh)
proxmox=true
;;
config/truenas-rdte-targets.json|scripts/truenas_rdte_target.py|scripts/gha_kvm_truenas_rdte.sh|scripts/truenas_installer_rpc_probe.py|scripts/truenas_installer_rpc_install.py|scripts/truenas_middleware_ddp_probe.py|scripts/truenas_middleware_pool_probe.py|scripts/truenas_middleware_app_probe.py|scripts/truenas_middleware_app_lifecycle_probe.py|scripts/truenas_middleware_litellm_t6_probe.py|scripts/truenas_middleware_wow_sidecar_t6_probe.py|scripts/truenas_middleware_garm_t6_probe.py|scripts/truenas_middleware_garm_provider_g2_probe.py)
config/truenas-rdte-targets.json|scripts/truenas_rdte_target.py|scripts/gha_kvm_truenas_rdte.sh|scripts/truenas_installer_rpc_probe.py|scripts/truenas_installer_rpc_install.py|scripts/truenas_middleware_ddp_probe.py|scripts/truenas_middleware_pool_probe.py|scripts/truenas_middleware_app_probe.py|scripts/truenas_middleware_app_lifecycle_probe.py|scripts/truenas_middleware_litellm_t6_probe.py|scripts/truenas_middleware_wow_sidecar_t6_probe.py|scripts/truenas_middleware_garm_t6_probe.py|scripts/truenas_middleware_garm_provider_g2_probe.py|scripts/truenas_middleware_garm_provider_g3_probe.py)
truenas=true
;;
scripts/gha_qemu_t3_topology_probe.py|.github/workflows/gha-kvm-system-rdte.yml)
Expand All @@ -118,7 +119,7 @@ jobs:
if [[ "$run_request_changed" == "true" ]]; then
truenas_version="$(jq -er '.version | select(type == "string" and length > 0)' "$RUN_REQUEST_PATH")"
truenas_rung="$(jq -er '.rung | select(. == "t0" or . == "t1" or . == "t2" or . == "t3" or . == "t4" or . == "t5" or . == "t6")' "$RUN_REQUEST_PATH")"
truenas_t6_product="$(jq -er '(.product // "litellm") | select(. == "litellm" or . == "wow-sidecar" or . == "garm" or . == "garm-provider-g2")' "$RUN_REQUEST_PATH")"
truenas_t6_product="$(jq -er '(.product // "litellm") | select(. == "litellm" or . == "wow-sidecar" or . == "garm" or . == "garm-provider-g2" or . == "garm-provider-g3")' "$RUN_REQUEST_PATH")"
jq -e '.schema == "gha-kvm-truenas-run-request/v1"' "$RUN_REQUEST_PATH" >/dev/null
python3 scripts/truenas_rdte_target.py \
--registry config/truenas-rdte-targets.json \
Expand All @@ -145,7 +146,7 @@ jobs:
with:
persist-credentials: false
- name: Validate public-safe contract
run: python3 -m unittest -v tests/test_gha_kvm_system_rdte.py tests/test_truenas_future_protocols.py tests/test_truenas_t6_litellm_control.py tests/test_truenas_t6_wow_sidecar_control.py tests/test_truenas_t6_garm_control.py tests/test_truenas_garm_provider_g2.py tests/test_truenas_rdte_target.py
run: python3 -m unittest -v tests/test_gha_kvm_system_rdte.py tests/test_truenas_future_protocols.py tests/test_truenas_t6_litellm_control.py tests/test_truenas_t6_wow_sidecar_control.py tests/test_truenas_t6_garm_control.py tests/test_truenas_garm_provider_g2.py tests/test_truenas_garm_provider_g3.py tests/test_truenas_rdte_target.py

foundry-t6-control:
needs: [changes, contract]
Expand Down Expand Up @@ -183,6 +184,15 @@ jobs:
permissions:
contents: read

provider-g3-fixture:
needs: [changes, contract]
if: needs.contract.result == 'success' && needs.changes.outputs.truenas == 'true' && needs.changes.outputs.truenas_rung == 't6' && needs.changes.outputs.truenas_t6_product == 'garm-provider-g3'
uses: SemperSupra/garm-provider-truenas/.github/workflows/export-g3-nested-fixture.yml@e92d4024e6e60c0e2f8bcf5301c11fccb14eaaab
with:
provider_ref: e92d4024e6e60c0e2f8bcf5301c11fccb14eaaab
permissions:
contents: read

qemu-t3-topology:
needs: [changes, contract]
if: needs.contract.result == 'success' && needs.changes.outputs.qemu_topology == 'true'
Expand Down Expand Up @@ -264,7 +274,7 @@ jobs:
retention-days: 7

truenas-26-beta3:
needs: [changes, contract, proxmox-9-2, foundry-t6-control, foundry-wow-t6-control, foundry-garm-t6-control, provider-g2-fixtures]
needs: [changes, contract, proxmox-9-2, foundry-t6-control, foundry-wow-t6-control, foundry-garm-t6-control, provider-g2-fixtures, provider-g3-fixture]
if: >-
always() &&
needs.contract.result == 'success' &&
Expand All @@ -275,7 +285,8 @@ jobs:
(needs.changes.outputs.truenas_t6_product == 'litellm' && needs['foundry-t6-control'].result == 'success') ||
(needs.changes.outputs.truenas_t6_product == 'wow-sidecar' && needs['foundry-wow-t6-control'].result == 'success') ||
(needs.changes.outputs.truenas_t6_product == 'garm' && needs['foundry-garm-t6-control'].result == 'success') ||
(needs.changes.outputs.truenas_t6_product == 'garm-provider-g2' && needs['provider-g2-fixtures'].result == 'success')
(needs.changes.outputs.truenas_t6_product == 'garm-provider-g2' && needs['provider-g2-fixtures'].result == 'success') ||
(needs.changes.outputs.truenas_t6_product == 'garm-provider-g3' && needs['provider-g3-fixture'].result == 'success')
)
runs-on: ubuntu-26.04
timeout-minutes: 50
Expand Down Expand Up @@ -307,6 +318,12 @@ jobs:
with:
name: garm-provider-truenas-g2-fixtures
path: ${{ runner.temp }}/gha-kvm-system-rdte/g2-fixtures
- name: Download exact GARM provider G3 fixture
if: needs.changes.outputs.truenas_rung == 't6' && needs.changes.outputs.truenas_t6_product == 'garm-provider-g3'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: garm-provider-truenas-g3-fixture
path: ${{ runner.temp }}/gha-kvm-system-rdte/g3-fixture
- name: Ensure bounded QEMU/KVM prerequisites
run: |
set -euo pipefail
Expand All @@ -330,6 +347,12 @@ jobs:
--g2-fixture-dir "$RUNNER_TEMP/gha-kvm-system-rdte/g2-fixtures"
--g2-fixture-producer "1667aa73e514c278e221082ac33f2f10a4141eef"
)
elif [[ "${{ needs.changes.outputs.truenas_t6_product }}" == "garm-provider-g3" ]]; then
args+=(
--t6-product "garm-provider-g3"
--g3-fixture-dir "$RUNNER_TEMP/gha-kvm-system-rdte/g3-fixture"
--g3-fixture-producer "e92d4024e6e60c0e2f8bcf5301c11fccb14eaaab"
)
else
if [[ "${{ needs.changes.outputs.truenas_t6_product }}" == "garm" ]]; then
foundry_commit="7fd0d2474248b61cf8e290fc80f40b761cea5e98"
Expand Down
28 changes: 26 additions & 2 deletions scripts/gha_kvm_truenas_rdte.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ MIN_HOST_MEM_KIB=$((11 * 1024 * 1024))
MIN_HOST_FREE_KIB=$((28 * 1024 * 1024))

usage() {
echo "Usage: gha_kvm_truenas_rdte.sh --out RECEIPT [--state-dir DIR] [--target-version VERSION] [--rung t0|t1|t2|t3|t4|t5|t6] [--t6-product litellm|wow-sidecar|garm|garm-provider-g2] [--foundry-control-dir DIR] [--foundry-commit SHA] [--g2-fixture-dir DIR] [--g2-fixture-producer SHA]"
echo "Usage: gha_kvm_truenas_rdte.sh --out RECEIPT [--state-dir DIR] [--target-version VERSION] [--rung t0|t1|t2|t3|t4|t5|t6] [--t6-product litellm|wow-sidecar|garm|garm-provider-g2|garm-provider-g3] [--foundry-control-dir DIR] [--foundry-commit SHA] [--g2-fixture-dir DIR] [--g2-fixture-producer SHA] [--g3-fixture-dir DIR] [--g3-fixture-producer SHA]"
}

OUT=""
Expand All @@ -28,6 +28,8 @@ FOUNDRY_CONTROL_DIR=""
FOUNDRY_COMMIT=""
G2_FIXTURE_DIR=""
G2_FIXTURE_PRODUCER=""
G3_FIXTURE_DIR=""
G3_FIXTURE_PRODUCER=""
while [[ $# -gt 0 ]]; do
case "$1" in
--out) OUT="$2"; shift 2 ;;
Expand All @@ -39,6 +41,8 @@ while [[ $# -gt 0 ]]; do
--foundry-commit) FOUNDRY_COMMIT="$2"; shift 2 ;;
--g2-fixture-dir) G2_FIXTURE_DIR="$2"; shift 2 ;;
--g2-fixture-producer) G2_FIXTURE_PRODUCER="$2"; shift 2 ;;
--g3-fixture-dir) G3_FIXTURE_DIR="$2"; shift 2 ;;
--g3-fixture-producer) G3_FIXTURE_PRODUCER="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;;
esac
Expand All @@ -51,11 +55,15 @@ eval "$TARGET_ENV"
[[ "$RUNG" == "t0" || "$RUNG" == "t1" || "$RUNG" == "t2" || "$RUNG" == "t3" || "$RUNG" == "t4" || "$RUNG" == "t5" || "$RUNG" == "t6" ]] || { echo "rung must be t0, t1, t2, t3, t4, t5, or t6" >&2; exit 2; }
if [[ "$RUNG" == "t6" ]]; then
[[ "$VERSION" == "26.0.0-BETA.3" ]] || { echo "T6 is currently admitted only for exact TrueNAS 26.0.0-BETA.3; use Foundry F1-F5 for other targets" >&2; exit 2; }
[[ "$T6_PRODUCT" == "litellm" || "$T6_PRODUCT" == "wow-sidecar" || "$T6_PRODUCT" == "garm" || "$T6_PRODUCT" == "garm-provider-g2" ]] || { echo "unsupported T6 product: $T6_PRODUCT" >&2; exit 2; }
[[ "$T6_PRODUCT" == "litellm" || "$T6_PRODUCT" == "wow-sidecar" || "$T6_PRODUCT" == "garm" || "$T6_PRODUCT" == "garm-provider-g2" || "$T6_PRODUCT" == "garm-provider-g3" ]] || { echo "unsupported T6 product: $T6_PRODUCT" >&2; exit 2; }
if [[ "$T6_PRODUCT" == "garm-provider-g2" ]]; then
[[ -n "$G2_FIXTURE_DIR" && -d "$G2_FIXTURE_DIR" ]] || { echo "garm-provider-g2 requires --g2-fixture-dir" >&2; exit 2; }
[[ "$G2_FIXTURE_PRODUCER" =~ ^[0-9a-f]{40}$ ]] || { echo "garm-provider-g2 requires exact --g2-fixture-producer SHA" >&2; exit 2; }
G2_FIXTURE_DIR="$(realpath "$G2_FIXTURE_DIR")"
elif [[ "$T6_PRODUCT" == "garm-provider-g3" ]]; then
[[ -n "$G3_FIXTURE_DIR" && -d "$G3_FIXTURE_DIR" ]] || { echo "garm-provider-g3 requires --g3-fixture-dir" >&2; exit 2; }
[[ "$G3_FIXTURE_PRODUCER" =~ ^[0-9a-f]{40}$ ]] || { echo "garm-provider-g3 requires exact --g3-fixture-producer SHA" >&2; exit 2; }
G3_FIXTURE_DIR="$(realpath "$G3_FIXTURE_DIR")"
else
[[ -n "$FOUNDRY_CONTROL_DIR" && -d "$FOUNDRY_CONTROL_DIR" ]] || { echo "t6 requires --foundry-control-dir" >&2; exit 2; }
[[ "$FOUNDRY_COMMIT" =~ ^[0-9a-f]{40}$ ]] || { echo "t6 requires exact --foundry-commit SHA" >&2; exit 2; }
Expand Down Expand Up @@ -129,6 +137,8 @@ payload = {
"app": (
{"name": "garm-provider-g2-fixtures", "image": "ghcr.io/actions/actions-runner:2.336.0@sha256:0cfdcc701ce933c6d243c6b0b2da767366dc9f2e99961d4c3754b0b78084cdda"}
if os.environ.get("R_RUNG") == "t6" and os.environ.get("R_T6_PRODUCT") == "garm-provider-g2"
else {"name": "garm-provider-g3-runner", "image": "ghcr.io/actions/actions-runner:2.336.0@sha256:0cfdcc701ce933c6d243c6b0b2da767366dc9f2e99961d4c3754b0b78084cdda"}
if os.environ.get("R_RUNG") == "t6" and os.environ.get("R_T6_PRODUCT") == "garm-provider-g3"
else {"name": "rdte-t6-garm", "image": "ghcr.io/sempersupra/garm-appliance@sha256:1af67841ddd4589e3798dcda8be49230565c849d07ab57fd05899432dcdabca9"}
if os.environ.get("R_RUNG") == "t6" and os.environ.get("R_T6_PRODUCT") == "garm"
else {"name": "rdte-t6-wow-sidecar", "image": "ghcr.io/sempersupra/wow-sidecar@sha256:6b700ce7ba5ae44116b240ccbb54fb3b60dc952a9b4072ca1314b6f311bc5376"}
Expand Down Expand Up @@ -223,6 +233,11 @@ if [[ "$RUNG" == "t2" || "$RUNG" == "t3" || "$RUNG" == "t4" || "$RUNG" == "t5" |
fail_evidence HARNESS_FAILURE preflight "missing TrueNAS GARM provider G2 client"
command -v docker >/dev/null 2>&1 || fail_evidence ENVIRONMENT_FAILURE preflight "missing prerequisite: docker"
command -v openssl >/dev/null 2>&1 || fail_evidence ENVIRONMENT_FAILURE preflight "missing prerequisite: openssl"
elif [[ "$T6_PRODUCT" == "garm-provider-g3" ]]; then
[[ -f "$SCRIPT_DIR/truenas_middleware_garm_provider_g3_probe.py" ]] ||
fail_evidence HARNESS_FAILURE preflight "missing TrueNAS GARM provider G3 client"
command -v docker >/dev/null 2>&1 || fail_evidence ENVIRONMENT_FAILURE preflight "missing prerequisite: docker"
command -v openssl >/dev/null 2>&1 || fail_evidence ENVIRONMENT_FAILURE preflight "missing prerequisite: openssl"
elif [[ "$T6_PRODUCT" == "garm" ]]; then
[[ -f "$SCRIPT_DIR/truenas_middleware_garm_t6_probe.py" ]] ||
fail_evidence HARNESS_FAILURE preflight "missing TrueNAS T6 GARM control client"
Expand Down Expand Up @@ -638,6 +653,13 @@ if [[ "$T6_PRODUCT" == "garm-provider-g2" ]]; then
--fixture-dir "$G2_FIXTURE_DIR" \
--fixture-producer-commit "$G2_FIXTURE_PRODUCER" \
--out "$FOUNDRY_OUT" --timeout 8 --job-timeout 300 >/dev/null 2>&1 || true
elif [[ "$T6_PRODUCT" == "garm-provider-g3" ]]; then
python3 "$SCRIPT_DIR/truenas_middleware_garm_provider_g3_probe.py" \
--host 127.0.0.1 --http-port "$HTTP_PORT" --https-port "$HTTPS_PORT" \
--password-file "$PASSWORD_FILE" \
--fixture-dir "$G3_FIXTURE_DIR" \
--fixture-producer-commit "$G3_FIXTURE_PRODUCER" \
--out "$FOUNDRY_OUT" --timeout 8 --job-timeout 300 >/dev/null 2>&1 || true
elif [[ "$T6_PRODUCT" == "garm" ]]; then
python3 "$SCRIPT_DIR/truenas_middleware_garm_t6_probe.py" \
--host 127.0.0.1 --port "$MIDDLEWARE_PORT" --service-port "$GARM_HOST_PORT" \
Expand Down Expand Up @@ -676,6 +698,8 @@ PY

if [[ "$T6_PRODUCT" == "garm-provider-g2" ]]; then
write_receipt SUPPORTED true foundry-materialization "exact packaged GARM TrueNAS provider passed verified WSS/API-key transport, valid local adoption, foreign exclusion, managed-drift fail-closed behavior, fresh-process readoption, and zero-residue cleanup; provider create/delete and GitHub/JIT intentionally not exercised"
elif [[ "$T6_PRODUCT" == "garm-provider-g3" ]]; then
write_receipt SUPPORTED true foundry-materialization "exact packaged GARM TrueNAS provider CreateInstance realized the source-derived fixed runner App profile, exact Compose read-back and provider Get/List reconciliation passed, supported middleware moved the experiment App inactive, provider DeleteInstance retired it, and GitHub/JIT/private workload/physical/capacity claims remained unexercised"
elif [[ "$T6_PRODUCT" == "garm" ]]; then
write_receipt SUPPORTED true foundry-materialization "exact Foundry-exported GARM controller control passed exact appliance and secret-normalized config read-back, persistent state, external HTTPS, restart persistence, and zero-residue cleanup; GitHub/JIT registration intentionally not exercised"
elif [[ "$T6_PRODUCT" == "wow-sidecar" ]]; then
Expand Down
Loading
Loading