@@ -3,7 +3,8 @@ name: VM Boot Smoke Test (Tier 2)
33# Tier 2: Real VM boot test on self-hosted KVM runner.
44# All checks run inside the guest via SSH (not host-side).
55#
6- # REQUIRES: self-hosted runner with KVM, QEMU, cloud-utils, and ssh-keygen.
6+ # REQUIRES: self-hosted runner with KVM, QEMU, libvirt/virt-install, cloud-utils,
7+ # and ssh-keygen.
78# Gate: only runs when vars.HAS_KVM_RUNNER == 'true'.
89#
910# SECURITY: Never triggered by pull_request (fork safety for self-hosted runners).
@@ -110,9 +111,9 @@ jobs:
110111 - name : Wait for SSH readiness
111112 run : |
112113 echo "Waiting for guest SSH (up to 5 minutes)..."
113- SSH_OPTS=" -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key"
114+ SSH_OPTS=( -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key)
114115 for i in $(seq 1 60); do
115- if ssh $ SSH_OPTS -p 2222 root@localhost echo "SSH ready" 2>/dev/null; then
116+ if ssh "${ SSH_OPTS[@]}" -p 2222 root@localhost echo "SSH ready" 2>/dev/null; then
116117 echo "Guest SSH is up after $((i * 5)) seconds"
117118 break
118119 fi
@@ -121,34 +122,34 @@ jobs:
121122
122123 - name : " Check: systemd system state"
123124 run : |
124- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
125- STATE=$($ SSH "systemctl is-system-running" 2>/dev/null || true)
125+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
126+ STATE=$("${ SSH[@]}" "systemctl is-system-running" 2>/dev/null || true)
126127 echo "System state: $STATE"
127128 if [ "$STATE" != "running" ] && [ "$STATE" != "degraded" ]; then
128129 echo "FAIL: expected running or degraded, got $STATE"
129- $ SSH "systemctl --failed" || true
130+ "${ SSH[@]}" "systemctl --failed" || true
130131 exit 1
131132 fi
132133
133134 - name : " Check: auth endpoint responds"
134135 run : |
135- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
136- $ SSH "curl -sf http://127.0.0.1:8480/api/auth/status" || {
136+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
137+ "${ SSH[@]}" "curl -sf http://127.0.0.1:8480/api/auth/status" || {
137138 echo "FAIL: auth endpoint did not respond"
138139 exit 1
139140 }
140141
141142 - name : " Check: health endpoint"
142143 run : |
143- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
144- $ SSH "curl -sf http://127.0.0.1:8480/health" || {
144+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
145+ "${ SSH[@]}" "curl -sf http://127.0.0.1:8480/health" || {
145146 echo "FAIL: health endpoint did not respond"
146147 exit 1
147148 }
148149
149150 - name : " Check: disabled services stay inactive (offline_private profile)"
150151 run : |
151- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
152+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
152153 DISABLED=(
153154 secure-ai-diffusion.service
154155 secure-ai-airlock.service
@@ -158,7 +159,7 @@ jobs:
158159 secure-ai-enable-diffusion.path
159160 )
160161 for svc in "${DISABLED[@]}"; do
161- if $ SSH "systemctl is-active --quiet $svc" 2>/dev/null; then
162+ if "${ SSH[@]}" "systemctl is-active --quiet $svc" 2>/dev/null; then
162163 echo "FAIL: $svc is active (should be disabled in offline_private)"
163164 exit 1
164165 fi
@@ -167,31 +168,31 @@ jobs:
167168
168169 - name : " Check: default profile is offline_private"
169170 run : |
170- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
171- PROFILE=$($ SSH "cat /var/lib/secure-ai/state/profile.json 2>/dev/null || echo '{}'")
171+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
172+ PROFILE=$("${ SSH[@]}" "cat /var/lib/secure-ai/state/profile.json 2>/dev/null || echo '{}'")
172173 echo "Profile state: $PROFILE"
173174 # On first boot without wizard, profile.json may not exist yet — fallback is offline_private
174175
175176 - name : " Check: vault API responds"
176177 run : |
177- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
178- $ SSH "curl -sf http://127.0.0.1:8480/api/vault/status" || {
178+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
179+ "${ SSH[@]}" "curl -sf http://127.0.0.1:8480/api/vault/status" || {
179180 echo "FAIL: vault status endpoint did not respond"
180181 exit 1
181182 }
182183
183184 - name : " Check: quarantine directory exists"
184185 run : |
185- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
186- $ SSH "test -d /var/lib/secure-ai/quarantine" || {
186+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
187+ "${ SSH[@]}" "test -d /var/lib/secure-ai/quarantine" || {
187188 echo "FAIL: quarantine directory does not exist"
188189 exit 1
189190 }
190191
191192 - name : " Check: rpm-ostree deployment"
192193 run : |
193- SSH=" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost"
194- $ SSH "rpm-ostree status" || {
194+ SSH=( ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/vm-smoke-key -p 2222 root@localhost)
195+ "${ SSH[@]}" "rpm-ostree status" || {
195196 echo "FAIL: rpm-ostree status failed"
196197 exit 1
197198 }
0 commit comments