@@ -17,10 +17,16 @@ jobs:
1717 go-version-file : go.mod
1818 cache : true
1919 - name : Run controller-runtime envtest
20- run : make test
20+ run : make test-report
21+ - name : Upload test reports
22+ if : always()
23+ uses : actions/upload-artifact@v4
24+ with :
25+ name : envtest-reports
26+ path : reports/
2127
2228 kind-e2e :
23- name : kind e2e
29+ name : kind topology e2e
2430 runs-on : ubuntu-latest
2531 steps :
2632 - uses : actions/checkout@v4
@@ -31,56 +37,123 @@ jobs:
3137 - uses : helm/kind-action@v1
3238 with :
3339 cluster_name : kind
34- - name : Run one deployment e2e test
40+ - name : Run deployed-topology e2e test
3541 env :
3642 RUN_E2E : " true"
3743 KIND_CLUSTER : kind
38- run : make test-e2e
44+ run : make test-e2e-report
45+ - name : Upload e2e reports
46+ if : always()
47+ uses : actions/upload-artifact@v4
48+ with :
49+ name : e2e-reports
50+ path : reports/
3951
40- smolvm-runtime-smoke :
41- name : smolvm runtime smoke
52+ smolvm-runtime-e2e :
53+ name : smolvm runtime lifecycle e2e
4254 runs-on : ubuntu-latest
43- timeout-minutes : 20
55+ timeout-minutes : 45
4456 steps :
4557 - uses : actions/checkout@v4
4658 - uses : actions/setup-go@v5
4759 with :
4860 go-version-file : go.mod
4961 cache : true
62+ - name : Prepare KVM-backed kind mounts
63+ run : |
64+ set -euxo pipefail
65+ test -e /dev/kvm
66+ sudo chmod 666 /dev/kvm
67+ sudo mkdir -p /tmp/smolvm-kind-var-lib /tmp/smolvm-kind-var-run
68+ sudo chmod 777 /tmp/smolvm-kind-var-lib /tmp/smolvm-kind-var-run
5069 - uses : helm/kind-action@v1
5170 with :
5271 cluster_name : runtime
53- - name : Install smolvm
72+ config : .github/kind-runtime-config.yaml
73+ - name : Build and load operator images
5474 run : |
55- sudo apt-get update
56- sudo apt-get install -y libvirglrenderer1 libepoxy0 libgbm1
57- curl -fsSL -o /tmp/smolvm.tar.gz https://github. com/smol-machines/ smolvm/releases/download/v0.8.1/smolvm-0.8.1-linux-x86_64.tar.gz
58- mkdir -p /tmp/ smolvm
59- tar -xzf /tmp/smolvm.tar.gz -C /tmp/ smolvm --strip-components=1
60- echo /tmp/ smolvm >> "$GITHUB_PATH"
61- /tmp/ smolvm/smolvm --version
62- - name : Run operator against real smolvm runtime
75+ set -euxo pipefail
76+ make docker-build IMG=example.com/smolvm-operator:e2e
77+ make docker-build-runtime RUNTIME_IMG=example. com/smolvm-runtime:e2e
78+ docker run --rm --entrypoint smolvm example.com/ smolvm-runtime:e2e --version
79+ docker run --rm --entrypoint /runtime-agent example.com/ smolvm-runtime:e2e --help
80+ kind load docker-image example.com/ smolvm-operator:e2e --name runtime
81+ kind load docker-image example.com/ smolvm-runtime:e2e --name runtime
82+ - name : Deploy full stack and run VM lifecycle
6383 run : |
6484 set -euxo pipefail
65- sudo chmod 666 /dev/kvm
66- export LD_LIBRARY_PATH="/tmp/smolvm/lib:${LD_LIBRARY_PATH:-}"
67- export LD_PRELOAD="$(find /usr/lib -name 'libvirglrenderer.so.1' | head -1)"
68- smolvm serve start --listen 127.0.0.1:8080 > smolvm-serve.log 2>&1 &
69- serve_pid=$!
70- trap 'kubectl delete smolvm runtime-smoke --ignore-not-found=true --wait=false || true; kill $serve_pid || true; cat smolvm-serve.log || true' EXIT
71-
72- for i in {1..60}; do
73- curl -fsS http://127.0.0.1:8080/api/v1/machines && break
74- sleep 1
75- done
85+ mkdir -p reports
86+ current_step="install"
87+ write_runtime_report() {
88+ local status="$1"
89+ local failed_step="${2:-}"
90+ if [ "$status" = "pass" ]; then
91+ cat > reports/runtime.xml <<'EOF'
92+ <testsuite name="smolvm runtime lifecycle e2e" tests="9" failures="0" errors="0">
93+ <testcase classname="runtime" name="install CRDs"/>
94+ <testcase classname="runtime" name="deploy controller Deployment and runtime DaemonSet"/>
95+ <testcase classname="runtime" name="runtime DaemonSet rolls out"/>
96+ <testcase classname="runtime" name="SmolVMNode reports Ready and RuntimeReady"/>
97+ <testcase classname="runtime" name="create unpinned SmolVM"/>
98+ <testcase classname="runtime" name="SmolVM is scheduled and Running"/>
99+ <testcase classname="runtime" name="patch spec.running false"/>
100+ <testcase classname="runtime" name="SmolVM reaches Stopped"/>
101+ <testcase classname="runtime" name="delete CR cleans runtime machine"/>
102+ </testsuite>
103+ EOF
104+ return
105+ fi
106+ cat > reports/runtime.xml <<EOF
107+ <testsuite name="smolvm runtime lifecycle e2e" tests="1" failures="1" errors="0">
108+ <testcase classname="runtime" name="${failed_step}">
109+ <failure message="runtime lifecycle e2e failed at ${failed_step}"/>
110+ </testcase>
111+ </testsuite>
112+ EOF
113+ }
114+ cleanup() {
115+ status=$?
116+ if [ ! -f reports/runtime.xml ]; then
117+ if [ "$status" -eq 0 ]; then
118+ write_runtime_report pass
119+ else
120+ write_runtime_report fail "$current_step"
121+ fi
122+ fi
123+ kubectl delete smolvm runtime-smoke --ignore-not-found=true --wait=false || true
124+ kubectl -n operator-system get pods -o wide || true
125+ kubectl -n operator-system logs deployment/operator-controller-manager --all-containers=true || true
126+ kubectl -n operator-system logs daemonset/operator-smolvm-runtime --all-containers=true || true
127+ exit "$status"
128+ }
129+ trap cleanup EXIT
76130
131+ current_step="install CRDs"
77132 make install
78- SMOLVM_API_URL=http://127.0.0.1:8080 go run ./cmd/main.go \
79- --metrics-bind-address=0 \
80- --health-probe-bind-address=:8081 > manager.log 2>&1 &
81- manager_pid=$!
82- trap 'kubectl delete smolvm runtime-smoke --ignore-not-found=true --wait=false || true; kill $manager_pid || true; kill $serve_pid || true; cat manager.log || true; cat smolvm-serve.log || true' EXIT
133+ current_step="deploy full operator stack"
134+ make deploy IMG=example.com/smolvm-operator:e2e RUNTIME_IMG=example.com/smolvm-runtime:e2e
135+
136+ current_step="controller Deployment rollout"
137+ kubectl rollout status deployment/operator-controller-manager -n operator-system --timeout=3m
138+ current_step="runtime DaemonSet rollout"
139+ kubectl rollout status daemonset/operator-smolvm-runtime -n operator-system --timeout=5m
83140
141+ current_step="SmolVMNode readiness"
142+ node=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
143+ for i in {1..90}; do
144+ kubectl get smolvmnode "$node" -o yaml || true
145+ ready=$(kubectl get smolvmnode "$node" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)
146+ runtime_ready=$(kubectl get smolvmnode "$node" -o jsonpath='{.status.conditions[?(@.type=="RuntimeReady")].status}' 2>/dev/null || true)
147+ if [ "$ready" = "True" ] && [ "$runtime_ready" = "True" ]; then
148+ break
149+ fi
150+ sleep 2
151+ done
152+ test "$(kubectl get smolvmnode "$node" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = "True"
153+ test "$(kubectl get smolvmnode "$node" -o jsonpath='{.status.conditions[?(@.type=="RuntimeReady")].status}')" = "True"
154+ test "$(kubectl get smolvmnode "$node" -o jsonpath='{.status.endpoint.podNamespace}')" = "operator-system"
155+
156+ current_step="create unpinned SmolVM"
84157 cat <<'EOF' | kubectl apply -f -
85158 apiVersion: vm.smolvm.dev/v1alpha1
86159 kind: SmolVM
@@ -94,40 +167,40 @@ jobs:
94167 memoryMiB: 128
95168 EOF
96169
97- ready=false
98- for i in {1..48}; do
99- echo "--- poll $i ---"
170+ current_step="SmolVM reaches Running"
171+ for i in {1..60}; do
100172 kubectl get smolvm runtime-smoke -o yaml || true
101- curl -fsS http://127.0.0.1:8080/api/v1/machines || true
102173 status=$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)
103174 if [ "$status" = "True" ]; then
104- ready=true
105175 break
106176 fi
107177 sleep 10
108178 done
109- if [ "$ready" != "true" ]; then
110- echo "runtime-smoke did not become Ready"
111- kubectl get smolvm runtime-smoke -o yaml || true
112- curl -fsS http://127.0.0.1:8080/api/v1/machines || true
113- exit 1
114- fi
179+ test "$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.conditions[?(@.type=="Scheduled")].reason}')" = "Bound"
115180 test "$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.conditions[?(@.type=="RuntimeReady")].status}')" = "True"
116- test "$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.conditions[?(@.type=="GuestReady")].status }')" = "Unknown "
181+ test "$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.phase }')" = "Running "
117182
183+ current_step="patch spec.running false"
118184 kubectl patch smolvm runtime-smoke --type=merge -p '{"spec":{"running":false}}'
119- kubectl wait smolvm runtime-smoke \
120- --for=jsonpath='{.status.phase}'=Stopped \
121- --timeout=5m
185+ current_step="SmolVM reaches Stopped"
186+ kubectl wait smolvm runtime-smoke --for=jsonpath='{.status.phase}'=Stopped --timeout=5m
122187
188+ current_step="delete CR cleans runtime machine"
123189 machine=$(kubectl get smolvm runtime-smoke -o jsonpath='{.status.machineName}')
190+ runtime_pod=$(kubectl -n operator-system get pod -l app.kubernetes.io/name=smolvm-runtime -o jsonpath='{.items[0].metadata.name}')
124191 kubectl delete smolvm runtime-smoke --wait=true --timeout=5m
125192 for i in {1..30}; do
126- if ! curl -fsS http://127.0.0.1:8080/api/v1/machines | grep -q "$machine"; then
193+ if ! kubectl -n operator-system exec "$runtime_pod" -- smolvm machine ls | grep -q "$machine"; then
127194 exit 0
128195 fi
129196 sleep 2
130197 done
131198 echo "runtime machine $machine still exists after CR deletion"
132- curl -fsS http://127.0.0.1:8080/api/v1/machines || true
199+ kubectl -n operator-system exec "$runtime_pod" -- smolvm machine ls || true
133200 exit 1
201+ - name : Upload runtime logs
202+ if : always()
203+ uses : actions/upload-artifact@v4
204+ with :
205+ name : runtime-e2e-logs
206+ path : reports/
0 commit comments