Creating Labs
Scripting System
Learn how to write setup, check, solve, and cleanup scripts for POV Demo labs.
Script Types
Each task has four script types that run at different points in the task lifecycle.
| Type | When It Runs | Purpose |
|---|---|---|
setup | Before the learner starts the task | Configure the VM, install tools, create initial state |
check | When the learner clicks "Check" | Validate the learner's work; exit 0 = pass, non-zero = fail |
solve | When the learner clicks "Solve" | Apply the correct solution idempotently |
cleanup | After the learner completes the task | Remove temporary state created by setup |
Lab-level scripts in
lab_scripts/ (named setup-{vm} and cleanup-{vm}) run once at provision/teardown time, not per-task.Naming Convention
Scripts are named {operation}-{vm-name}. The VM name must match the name field in config.yml.
| Script filename | VM name in config.yml |
|---|---|
setup-ubuntu-1 | ubuntu-1 |
check-nomad-server-1 | nomad-server-1 |
solve-kubernetes-control-1 | kubernetes-control-1 |
Setup Scripts
Setup scripts configure the environment before the learner begins. Use #!/bin/bash -l to load the login shell profile.
#!/bin/bash -l
set -e
# Install required tools
apt-get update -q
apt-get install -y -q curl wget jq
# Create initial directory structure
mkdir -p /home/user/workspace
chown user:user /home/user/workspace
# Pre-populate a config file the learner will modify
cat > /home/user/workspace/config.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data: {}
EOF
# Add setup commands to bash history so learner can reference them
history -s "kubectl apply -f config.yaml"- Use
#!/bin/bash -l(login shell) to ensure the PATH includes all installed tools - Use
set -eso the script fails fast on errors - Make scripts idempotent — they may run multiple times
- Use
history -s "command"to pre-populate terminal history (NOTset -o history)
Check Scripts
Check scripts validate the learner's work. They must exit 0 on success and non-zero on failure. Use the provided check_template.sh helper.
#!/bin/bash -l
source /etc/profile.d/check_functions.sh
# Check that kubectl is configured correctly
check_command "kubectl cluster-info" "kubectl cluster-info should succeed"
# Check that the deployment exists
check_command "kubectl get deployment my-app -n default" \
"Deployment 'my-app' should exist in the default namespace"
# Check specific content in a file
check_file_content "/home/user/workspace/config.yaml" \
"name: my-config" \
"ConfigMap name should be 'my-config'"
echo "All checks passed!"Solve Scripts
Warning: Solve scripts MUST have
set -e on line 2. This is the most common lab validation failure.#!/bin/bash -l
set -e
# Apply the solution
kubectl apply -f /home/user/workspace/solution.yaml
# Verify the solution worked
kubectl wait --for=condition=available deployment/my-app --timeout=60s- Must be idempotent — running it twice must not break anything
- Use
history -s "command"to show the solution commands in terminal history (NOTset -o history)
Helper Functions
Source /etc/profile.d/check_functions.sh in check scripts to access these helper functions.
| Function | Signature | Description |
|---|---|---|
check_command | check_command "cmd" "message" | Runs cmd; fails with message if exit code is non-zero. |
check_file_content | check_file_content "file" "pattern" "message" | Fails if pattern is not found in file. |
fail | fail "message" | Immediately fails the check with message. |
ensure_history_flushed | ensure_history_flushed | Call at end of setup scripts to flush history to disk. |
AI Gateway
Each lab session automatically has access to an AI gateway for building AI-powered lab scenarios.
| Variable | Value |
|---|---|
AI_GATEWAY_URL | Injected at provision time |
AI_GATEWAY_TOKEN | Session-scoped bearer token |
AI_MODEL | Default model (e.g. claude-3-5-sonnet) |
Usage limits
- $5 per session credit limit
- 20 requests per minute rate limit
- Available inside lab VM scripts and task terminals
# Call AI from a lab script
response=$(curl -s -X POST "$AI_GATEWAY_URL/v1/messages" \
-H "Authorization: Bearer $AI_GATEWAY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "'"$AI_MODEL"'",
"max_tokens": 500,
"messages": [{"role": "user", "content": "Explain this error: ..."}]
}')
echo "$response" | jq -r '.content[0].text'