Skip to content

Commit 37667bd

Browse files
committed
feat(examples): add tiny smoke assets and validation
1 parent 5d978ba commit 37667bd

9 files changed

Lines changed: 217 additions & 0 deletions

File tree

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@ on:
55
pull_request:
66

77
jobs:
8+
tiny-smoke-assets:
9+
name: Validate tiny smoke assets
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Clone the code
13+
uses: actions/checkout@v6
14+
15+
- name: Verify tiny smoke example
16+
run: ./hack/verify-tiny-smoke.sh
17+
818
test:
919
name: Run on Ubuntu
1020
runs-on: ubuntu-latest
21+
needs: tiny-smoke-assets
1122
steps:
1223
- name: Clone the code
1324
uses: actions/checkout@v6

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ Each example is designed to be:
2929
- `isolated-untrusted-builds`: Disposable VM isolation boundary for risky builds
3030
- `compliance-segmented-workloads`: Sensitive workloads pinned to dedicated nodes
3131
- `cost-optimized-night-jobs`: Scale-from-zero capacity for periodic spikes
32+
- `tiny-smoke`: Tiny-footprint classRef boot + two-VM connectivity validation
3233

3334
## Notes
3435

3536
- These examples intentionally use `ghcr.io/syscode-labs/test:latest` as a neutral placeholder image.
37+
- `tiny-smoke` uses pinned Docker Hub images so it can run unchanged in public environments.
3638
- Secrets referenced by runner pool examples must be created separately.
3739
- GPU passthrough is not supported in `imp`/Firecracker here; examples focus on CPU/memory/storage-isolated workloads.

examples/tiny-smoke/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# tiny-smoke
2+
3+
## What This Demonstrates
4+
5+
A constrained-footprint smoke path for validating:
6+
- classRef-based VM boot
7+
- VM-to-VM east-west traffic on one `ImpNetwork`
8+
9+
This is intended for tiny nodes and one-off validation, not production sizing.
10+
11+
## Manifests
12+
13+
- `impvmclass.yaml`: tiny compute profile
14+
- `impnetwork.yaml`: isolated smoke network
15+
- `vm-server.yaml`: long-running server VM
16+
- `vm-client.yaml`: long-running client VM
17+
18+
## Prerequisites
19+
20+
- `imp` is installed and healthy.
21+
- Guest agent is enabled (default).
22+
- Agent can run Firecracker on at least one node.
23+
- `kubectl` and `curl` are installed on your workstation.
24+
25+
## Run Smoke Validation
26+
27+
```sh
28+
bash examples/tiny-smoke/run.sh
29+
```
30+
31+
What `run.sh` does:
32+
1. Applies class, network, and two VMs.
33+
2. Waits for both VMs to reach `Running`.
34+
3. Finds the `imp-agent` pod on the client VM node.
35+
4. Port-forwards the agent API.
36+
5. Executes an HTTP request from client VM to server VM over the ImpNetwork.
37+
6. Fails unless guest-exec returns exit code `0`.
38+
39+
## Cleanup
40+
41+
```sh
42+
kubectl delete -f examples/tiny-smoke/
43+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: imp.dev/v1alpha1
2+
kind: ImpNetwork
3+
metadata:
4+
name: tiny-smoke-net
5+
namespace: default
6+
spec:
7+
subnet: 10.251.0.0/24
8+
gateway: 10.251.0.1
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: imp.dev/v1alpha1
2+
kind: ImpVMClass
3+
metadata:
4+
name: tiny-smoke
5+
spec:
6+
vcpu: 1
7+
memoryMiB: 192

examples/tiny-smoke/run.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5+
EXAMPLE_DIR="${ROOT_DIR}/examples/tiny-smoke"
6+
NS="${NS:-default}"
7+
IMP_NS="${IMP_NS:-imp-system}"
8+
SERVER_VM="${SERVER_VM:-tiny-server}"
9+
CLIENT_VM="${CLIENT_VM:-tiny-client}"
10+
LOCAL_AGENT_PORT="${LOCAL_AGENT_PORT:-19091}"
11+
12+
require() {
13+
command -v "$1" >/dev/null 2>&1 || {
14+
echo "missing required command: $1" >&2
15+
exit 1
16+
}
17+
}
18+
19+
require kubectl
20+
require curl
21+
22+
echo "[1/6] apply tiny smoke manifests"
23+
kubectl apply -f "${EXAMPLE_DIR}/impvmclass.yaml"
24+
kubectl apply -f "${EXAMPLE_DIR}/impnetwork.yaml" -n "${NS}"
25+
kubectl apply -f "${EXAMPLE_DIR}/vm-server.yaml" -n "${NS}"
26+
kubectl apply -f "${EXAMPLE_DIR}/vm-client.yaml" -n "${NS}"
27+
28+
echo "[2/6] wait for both VMs to be Running"
29+
kubectl wait --for=jsonpath='{.status.phase}'=Running "impvm/${SERVER_VM}" -n "${NS}" --timeout=8m
30+
kubectl wait --for=jsonpath='{.status.phase}'=Running "impvm/${CLIENT_VM}" -n "${NS}" --timeout=8m
31+
32+
SERVER_IP="$(kubectl get impvm "${SERVER_VM}" -n "${NS}" -o jsonpath='{.status.ip}')"
33+
CLIENT_NODE="$(kubectl get impvm "${CLIENT_VM}" -n "${NS}" -o jsonpath='{.spec.nodeName}')"
34+
35+
if [[ -z "${SERVER_IP}" || -z "${CLIENT_NODE}" ]]; then
36+
echo "failed to resolve server IP or client node" >&2
37+
exit 1
38+
fi
39+
40+
echo "[3/6] locate imp-agent pod on client node ${CLIENT_NODE}"
41+
AGENT_POD="$(
42+
kubectl get pods -n "${IMP_NS}" -l app.kubernetes.io/component=agent \
43+
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\n"}{end}' \
44+
| awk -v node="${CLIENT_NODE}" '$2==node {print $1; exit}'
45+
)"
46+
47+
if [[ -z "${AGENT_POD}" ]]; then
48+
echo "no imp-agent pod found on node ${CLIENT_NODE} in namespace ${IMP_NS}" >&2
49+
exit 1
50+
fi
51+
52+
cleanup() {
53+
if [[ -n "${PF_PID:-}" ]]; then
54+
kill "${PF_PID}" >/dev/null 2>&1 || true
55+
fi
56+
}
57+
trap cleanup EXIT
58+
59+
echo "[4/6] port-forward agent API via pod/${AGENT_POD}"
60+
kubectl -n "${IMP_NS}" port-forward "pod/${AGENT_POD}" "${LOCAL_AGENT_PORT}:9091" >/tmp/imp-tiny-smoke-portforward.log 2>&1 &
61+
PF_PID=$!
62+
sleep 2
63+
64+
echo "[5/6] execute connectivity check from ${CLIENT_VM} -> ${SERVER_IP}"
65+
REQ_BODY="$(
66+
cat <<EOF
67+
{"command":["/bin/sh","-lc","wget -qO- http://${SERVER_IP} | grep -qi nginx"]}
68+
EOF
69+
)"
70+
RESP="$(
71+
curl -fsS -X POST \
72+
-H "Content-Type: application/json" \
73+
--data "${REQ_BODY}" \
74+
"http://127.0.0.1:${LOCAL_AGENT_PORT}/v1/exec/${NS}/${CLIENT_VM}"
75+
)"
76+
77+
echo "${RESP}" | grep -q '"stream":"exit","code":0' || {
78+
echo "connectivity check failed; agent exec response:" >&2
79+
echo "${RESP}" >&2
80+
exit 1
81+
}
82+
83+
echo "[6/6] smoke PASS: classRef boot + VM-to-VM HTTP connectivity validated"

examples/tiny-smoke/vm-client.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: imp.dev/v1alpha1
2+
kind: ImpVM
3+
metadata:
4+
name: tiny-client
5+
namespace: default
6+
spec:
7+
classRef:
8+
name: tiny-smoke
9+
networkRef:
10+
name: tiny-smoke-net
11+
image: docker.io/library/nginx:1.27.4-alpine
12+
lifecycle: persistent

examples/tiny-smoke/vm-server.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: imp.dev/v1alpha1
2+
kind: ImpVM
3+
metadata:
4+
name: tiny-server
5+
namespace: default
6+
spec:
7+
classRef:
8+
name: tiny-smoke
9+
networkRef:
10+
name: tiny-smoke-net
11+
image: docker.io/library/nginx:1.27.4-alpine
12+
lifecycle: persistent

hack/verify-tiny-smoke.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
EXAMPLE_DIR="${ROOT_DIR}/examples/tiny-smoke"
6+
7+
required_files=(
8+
"${EXAMPLE_DIR}/README.md"
9+
"${EXAMPLE_DIR}/impvmclass.yaml"
10+
"${EXAMPLE_DIR}/impnetwork.yaml"
11+
"${EXAMPLE_DIR}/vm-server.yaml"
12+
"${EXAMPLE_DIR}/vm-client.yaml"
13+
"${EXAMPLE_DIR}/run.sh"
14+
)
15+
16+
for f in "${required_files[@]}"; do
17+
[[ -f "${f}" ]] || {
18+
echo "missing tiny-smoke asset: ${f}" >&2
19+
exit 1
20+
}
21+
done
22+
23+
grep -q '^kind: ImpVMClass$' "${EXAMPLE_DIR}/impvmclass.yaml"
24+
grep -q '^kind: ImpNetwork$' "${EXAMPLE_DIR}/impnetwork.yaml"
25+
grep -q '^kind: ImpVM$' "${EXAMPLE_DIR}/vm-server.yaml"
26+
grep -q '^kind: ImpVM$' "${EXAMPLE_DIR}/vm-client.yaml"
27+
grep -q 'classRef:' "${EXAMPLE_DIR}/vm-server.yaml"
28+
grep -q 'classRef:' "${EXAMPLE_DIR}/vm-client.yaml"
29+
grep -q 'networkRef:' "${EXAMPLE_DIR}/vm-server.yaml"
30+
grep -q 'networkRef:' "${EXAMPLE_DIR}/vm-client.yaml"
31+
32+
# Require pinned image tags (reject :latest) for this tiny smoke path.
33+
if grep -R --line-number 'image: .*:latest' "${EXAMPLE_DIR}"/*.yaml; then
34+
echo "tiny-smoke manifests must not use :latest tags" >&2
35+
exit 1
36+
fi
37+
38+
bash -n "${EXAMPLE_DIR}/run.sh"
39+
echo "tiny-smoke assets verified"

0 commit comments

Comments
 (0)