Skip to content
This repository was archived by the owner on Jan 22, 2026. It is now read-only.

Commit d24afb4

Browse files
committed
Experimental add-hooks tests
1 parent 79a1a8d commit d24afb4

File tree

5 files changed

+277
-0
lines changed

5 files changed

+277
-0
lines changed

exporter-hooks-endlease.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export:
2+
power:
3+
type: jumpstarter_driver_power.driver.MockPower
4+
5+
hooks:
6+
beforeLease:
7+
script: |
8+
echo "HOOK: Failing to test endLease"
9+
exit 1
10+
timeout: 10
11+
onFailure: endLease

exporter-hooks-exit.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export:
2+
power:
3+
type: jumpstarter_driver_power.driver.MockPower
4+
5+
hooks:
6+
beforeLease:
7+
script: |
8+
echo "HOOK: Failing to test exit"
9+
exit 1
10+
timeout: 10
11+
onFailure: exit

exporter-hooks.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
metadata:
2+
name: test-exporter-hooks
3+
namespace: default
4+
5+
export:
6+
power:
7+
type: jumpstarter_driver_power.driver.MockPower
8+
storage:
9+
type: jumpstarter_driver_opendal.driver.MockStorageMux
10+
11+
hooks:
12+
beforeLease:
13+
script: |
14+
echo "HOOK: beforeLease starting"
15+
echo "LEASE_NAME=$LEASE_NAME"
16+
echo "CLIENT_NAME=$CLIENT_NAME"
17+
echo "JUMPSTARTER_HOST=$JUMPSTARTER_HOST"
18+
echo "Testing driver interaction..."
19+
j power on && echo "Power turned on successfully"
20+
echo "HOOK: beforeLease completed"
21+
timeout: 60
22+
onFailure: warn
23+
afterLease:
24+
script: |
25+
echo "HOOK: afterLease starting"
26+
echo "Testing driver interaction..."
27+
j power off && echo "Power turned off successfully"
28+
echo "HOOK: afterLease completed"
29+
timeout: 60
30+
onFailure: warn

k8s-exporter-hooks.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: jumpstarter.dev/v1alpha1
2+
kind: Exporter
3+
metadata:
4+
name: test-exporter-hooks
5+
namespace: jumpstarter-lab
6+
spec:
7+
export:
8+
power:
9+
type: jumpstarter_driver_power.driver.MockPower
10+
storage:
11+
type: jumpstarter_driver_opendal.driver.MockStorageMux
12+
hooks:
13+
beforeLease:
14+
script: |
15+
echo "HOOK: beforeLease starting"
16+
echo "LEASE_NAME=$LEASE_NAME"
17+
echo "CLIENT_NAME=$CLIENT_NAME"
18+
echo "JUMPSTARTER_HOST=$JUMPSTARTER_HOST"
19+
echo "Testing driver interaction..."
20+
j power on && echo "Power turned on successfully"
21+
echo "HOOK: beforeLease completed"
22+
timeout: 60
23+
onFailure: warn
24+
afterLease:
25+
script: |
26+
echo "HOOK: afterLease starting"
27+
echo "Testing driver interaction..."
28+
j power off && echo "Power turned off successfully"
29+
echo "HOOK: afterLease completed"
30+
timeout: 60
31+
onFailure: warn

tests.bats

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,33 @@ wait_for_exporter() {
1919
exporters.jumpstarter.dev/test-exporter-legacy
2020
}
2121

22+
wait_for_exporter_hooks() {
23+
sleep 2
24+
kubectl -n "${JS_NAMESPACE}" wait --timeout 20m --for=condition=Online --for=condition=Registered \
25+
exporters.jumpstarter.dev/test-exporter-hooks
26+
}
27+
28+
wait_for_exporter_status() {
29+
local exporter=$1
30+
local expected_status=$2
31+
local timeout=${3:-60}
32+
local interval=2
33+
local elapsed=0
34+
35+
while [ $elapsed -lt $timeout ]; do
36+
local status=$(kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/$exporter \
37+
-o jsonpath='{.status.exporterStatus}' 2>/dev/null || echo "Unknown")
38+
if [ "$status" = "$expected_status" ]; then
39+
return 0
40+
fi
41+
sleep $interval
42+
elapsed=$((elapsed + interval))
43+
done
44+
45+
echo "Timeout waiting for exporter $exporter to reach status $expected_status (current: $status)"
46+
return 1
47+
}
48+
2249
@test "can create clients with admin cli" {
2350
jmp admin create client -n "${JS_NAMESPACE}" test-client-oidc --unsafe --out /dev/null \
2451
--oidc-username dex:test-client-oidc
@@ -144,6 +171,173 @@ EOF
144171
jmp admin get lease --namespace "${JS_NAMESPACE}"
145172
}
146173

174+
# ============================================================================
175+
# HOOKS TESTS
176+
# ============================================================================
177+
178+
@test "hooks: can create hooks-enabled exporters with admin cli" {
179+
jmp admin create exporter -n "${JS_NAMESPACE}" test-exporter-hooks --save \
180+
--label example.com/board=hooks
181+
jmp admin create exporter -n "${JS_NAMESPACE}" test-exporter-hooks-endlease --save \
182+
--label example.com/board=hooks-endlease
183+
jmp admin create exporter -n "${JS_NAMESPACE}" test-exporter-hooks-exit --save \
184+
--label example.com/board=hooks-exit
185+
}
186+
187+
@test "hooks: can configure exporters with hooks" {
188+
go run github.com/mikefarah/yq/v4@latest -i ". * load(\"$GITHUB_ACTION_PATH/exporter-hooks.yaml\")" \
189+
/etc/jumpstarter/exporters/test-exporter-hooks.yaml
190+
go run github.com/mikefarah/yq/v4@latest -i ". * load(\"$GITHUB_ACTION_PATH/exporter-hooks-endlease.yaml\")" \
191+
/etc/jumpstarter/exporters/test-exporter-hooks-endlease.yaml
192+
go run github.com/mikefarah/yq/v4@latest -i ". * load(\"$GITHUB_ACTION_PATH/exporter-hooks-exit.yaml\")" \
193+
/etc/jumpstarter/exporters/test-exporter-hooks-exit.yaml
194+
195+
# Verify hooks are configured in the file
196+
run go run github.com/mikefarah/yq/v4@latest '.hooks.beforeLease.script' \
197+
/etc/jumpstarter/exporters/test-exporter-hooks.yaml
198+
assert_success
199+
assert_output --partial "beforeLease starting"
200+
}
201+
202+
@test "hooks: can run hooks-enabled exporter" {
203+
cat <<EOF | bash 3>&- &
204+
while true; do
205+
jmp run --exporter test-exporter-hooks
206+
done
207+
EOF
208+
209+
wait_for_exporter_hooks
210+
}
211+
212+
@test "hooks: beforeLease executes on lease acquisition" {
213+
wait_for_exporter_hooks
214+
215+
jmp config client use test-client-legacy
216+
217+
# Create lease - this should trigger beforeLease hook
218+
jmp create lease --selector example.com/board=hooks --duration 5m
219+
220+
# Wait for hook to execute and status to transition
221+
sleep 5
222+
223+
# Status should be LeaseReady after successful beforeLease hook
224+
local status=$(kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks \
225+
-o jsonpath='{.status.exporterStatus}')
226+
echo "Exporter status: $status"
227+
[ "$status" = "LeaseReady" ]
228+
229+
# Clean up
230+
jmp delete leases --all
231+
}
232+
233+
@test "hooks: can interact with drivers via j CLI in hook script" {
234+
wait_for_exporter_hooks
235+
236+
# The beforeLease hook runs "j power on" - if it fails, the hook would fail
237+
# We verify successful hook execution by checking the lease was acquired
238+
jmp shell --client test-client-legacy --selector example.com/board=hooks j power on
239+
240+
# If we get here, the hook successfully interacted with the driver
241+
wait_for_exporter_hooks
242+
}
243+
244+
@test "hooks: afterLease executes on lease release" {
245+
wait_for_exporter_hooks
246+
247+
jmp config client use test-client-legacy
248+
249+
# Create and immediately delete a lease to trigger both hooks
250+
jmp create lease --selector example.com/board=hooks --duration 1m
251+
sleep 5 # Wait for beforeLease hook
252+
253+
# Delete the lease to trigger afterLease hook
254+
jmp delete leases --all
255+
256+
# Wait for afterLease to complete and status to return to Available
257+
sleep 5
258+
259+
local status=$(kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks \
260+
-o jsonpath='{.status.exporterStatus}')
261+
echo "Exporter status after lease release: $status"
262+
263+
wait_for_exporter_hooks
264+
}
265+
266+
@test "hooks: onFailure=endLease blocks lease acquisition" {
267+
# Start the endLease failure mode exporter
268+
cat <<EOF | bash 3>&- &
269+
while true; do
270+
jmp run --exporter test-exporter-hooks-endlease
271+
done
272+
EOF
273+
274+
# Wait for exporter to be online
275+
sleep 5
276+
kubectl -n "${JS_NAMESPACE}" wait --timeout 2m --for=condition=Online --for=condition=Registered \
277+
exporters.jumpstarter.dev/test-exporter-hooks-endlease || true
278+
279+
jmp config client use test-client-legacy
280+
281+
# Try to create a lease - beforeLease hook will fail with onFailure=endLease
282+
jmp create lease --selector example.com/board=hooks-endlease --duration 1m || true
283+
284+
# Wait for hook to execute and fail
285+
sleep 10
286+
287+
# Status should show BeforeLeaseHookFailed
288+
local status=$(kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks-endlease \
289+
-o jsonpath='{.status.exporterStatus}')
290+
echo "Exporter status: $status"
291+
292+
[ "$status" = "BeforeLeaseHookFailed" ]
293+
294+
jmp delete leases --all || true
295+
}
296+
297+
@test "hooks: onFailure=exit shuts down exporter" {
298+
# Start the exit failure mode exporter (NOT in a loop - we want it to stay down)
299+
jmp run --exporter test-exporter-hooks-exit &
300+
EXPORTER_PID=$!
301+
302+
# Wait for exporter to be online
303+
sleep 5
304+
kubectl -n "${JS_NAMESPACE}" wait --timeout 2m --for=condition=Online --for=condition=Registered \
305+
exporters.jumpstarter.dev/test-exporter-hooks-exit || true
306+
307+
jmp config client use test-client-legacy
308+
309+
# Try to create a lease - beforeLease hook will fail with onFailure=exit
310+
jmp create lease --selector example.com/board=hooks-exit --duration 1m || true
311+
312+
# Wait for exporter to shutdown
313+
sleep 10
314+
315+
# The exporter should have gone offline
316+
local status=$(kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks-exit \
317+
-o jsonpath='{.status.exporterStatus}' 2>/dev/null || echo "Offline")
318+
echo "Exporter status: $status"
319+
320+
# Status should be Offline or BeforeLeaseHookFailed
321+
[[ "$status" = "Offline" ]] || [[ "$status" = "BeforeLeaseHookFailed" ]]
322+
323+
jmp delete leases --all || true
324+
kill $EXPORTER_PID 2>/dev/null || true
325+
}
326+
327+
@test "hooks: can delete hooks exporters with admin cli" {
328+
jmp admin delete exporter --namespace "${JS_NAMESPACE}" test-exporter-hooks --delete || true
329+
jmp admin delete exporter --namespace "${JS_NAMESPACE}" test-exporter-hooks-endlease --delete || true
330+
jmp admin delete exporter --namespace "${JS_NAMESPACE}" test-exporter-hooks-exit --delete || true
331+
332+
run ! kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks 2>/dev/null
333+
run ! kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks-endlease 2>/dev/null
334+
run ! kubectl -n "${JS_NAMESPACE}" get exporters.jumpstarter.dev/test-exporter-hooks-exit 2>/dev/null
335+
}
336+
337+
# ============================================================================
338+
# CLEANUP TESTS
339+
# ============================================================================
340+
147341
@test "can delete clients with admin cli" {
148342
kubectl -n "${JS_NAMESPACE}" get secret test-client-oidc-client
149343
kubectl -n "${JS_NAMESPACE}" get clients.jumpstarter.dev/test-client-oidc

0 commit comments

Comments
 (0)