02 — Interactive Building Blocks#
One runnable example per block type. Every block on this page is live — click the buttons to verify your environment and see what each block looks like from the learner's perspective.
See docs/AUTHORING.md for full schema reference and docs/REFERENCE_KUBERNETES_101.md for every pattern extracted from the reference training.
1. Shell Verification — non-interactive "run-and-verify"#
Use when: you want to check that a command produces a specific output without the learner having to run it manually.
How to use: Write a shell command that returns a count (via grep -c) or a non-empty string. Set expect.operator and expect.value accordingly.
The block below verifies the k3d cluster node is Ready:
<!-- LAB_QUESTION
type: shell-verification
question: "Verify the cluster node is Ready"
buttonText: "Check Cluster"
command: "kubectl get nodes --no-headers 2>/dev/null | grep -c ' Ready'"
expect:
operator: gt
value: 0
hint: "Wait 30 seconds after the Codespace starts and try again. The cluster provisions automatically."
explanation: "Cluster node is Ready — proceed to the next step."
-->
2. kubectl — interactive (Terminal tab)#
Use when: the learner should type kubectl commands themselves in the Terminal tab to explore the cluster. No block needed — the Terminal tab is always available. Document the command in the lesson and explain what to expect.
How to author:
You should see pods in kube-system and any namespaces your training deploys.
Pair with a `shell-verification` block to gate progression on the expected output:
<!-- LAB_QUESTION
type: shell-verification
question: "Verify the todoapp pods are Running"
buttonText: "Check todoapp"
command: "kubectl get pods -n todoapp --no-headers 2>/dev/null | grep -c Running"
expect:
operator: gt
value: 0
hint: "Run `kubectl get pods -n todoapp` in the Terminal tab to see the current status."
explanation: "todoapp pods are Running — the demo application is ready."
-->
---
## 3. kubectl — non-interactive (run-and-verify)
Use when: you want to validate a kubectl output without requiring the learner to run it interactively. Write the full command in `command` using jsonpath, grep, or awk to extract the value you need.
**Pattern: count Running pods**
```bash
# In command field:
kubectl get pods -n <namespace> --no-headers 2>/dev/null | grep -c Running
Pattern: check an annotation
# In command field:
kubectl get pods -n todoapp -o jsonpath='{.items[*].metadata.annotations.oneagent\.dynatrace\.com/injected}' 2>/dev/null | tr ' ' '\n' | grep -c true
Pattern: verify a CRD exists
4. Custom Helper Functions#
Use when: you need repeatable setup steps, environment manipulation, or training scenarios that go beyond what kubectl provides. Define functions in .devcontainer/util/my_functions.sh — they are sourced into every terminal session.
How to author:
# .devcontainer/util/my_functions.sh
# Function available interactively in the Terminal tab
injectFault(){
printInfoSection "Injecting a synthetic failure into the todoapp"
kubectl scale deployment todoapp -n todoapp --replicas=0
printInfo "todoapp scaled to 0 replicas — check your Dynatrace dashboard"
}
# Function to restore normal state
restoreApp(){
printInfoSection "Restoring todoapp to normal"
kubectl scale deployment todoapp -n todoapp --replicas=1
kubectl rollout status deployment/todoapp -n todoapp
printInfo "todoapp restored"
}
In the lesson, tell the learner to run the function in the Terminal tab:
Watch your Dynatrace dashboard — within 60 seconds you should see the service disappear from the Services view.
Use `STEP_SETUP` to call a function automatically before the page renders:
```markdown
<!-- STEP_SETUP
commands:
- mySetupFunction
-->
The check below calls the template's example custom function:
5. DQL Query — inline read-only#
Use when: you want to show learners an example DQL query to explore their tenant. No validation required — just document the query in a dql code block.
How to author:
Run this DQL query in **Notebooks** to explore your logs:
```dql
fetch logs
| filter k8s.namespace.name == "todoapp"
| filter timestamp > now() - 10m
| limit 10
```
6. DQL Verification — query with assertion#
Use when: you want to gate lesson progression on a Dynatrace entity or observability state. The learner's tenant must return a result that matches expect.
expect.operator: not-empty — passes if DQL returns at least one row:
<!-- LAB_QUESTION
type: dql-verification
question: "Verify Dynatrace is collecting logs from todoapp"
buttonText: "Check DT Logs"
dql: |
fetch logs
| filter k8s.namespace.name == "todoapp"
| filter timestamp > now() - 10m
| limit 1
expect:
operator: not-empty
hint: "Interact with the app first to generate some logs, then wait 2 minutes."
explanation: "Dynatrace is collecting logs — observability is active."
-->
expect.operator: gte with field — passes if a DQL aggregation meets a threshold:
<!-- LAB_QUESTION
type: dql-verification
question: "Verify Dynatrace discovered the todoapp namespace"
buttonText: "Validate"
dql: "fetch dt.entity.cloud_application_namespace | filter matchesPhrase(entity.name, \"todoapp\") | summarize count = count()"
expect:
operator: gte
field: count
value: 1
hint: "Navigate to Kubernetes in Dynatrace to visually confirm the namespace is visible."
explanation: "Dynatrace discovered the todoapp namespace — entity data is flowing."
-->
7. Assessment (multiple-choice + DQL, scored)#
Use when: you want to formally score the learner at the end of a lesson section. Create a .assessment/<id>.json file and bind it with boundScenarioId.
Step 1 — Create the assessment JSON:
// .assessment/my-training-quiz.json
{
"templateVersion": "1.0.0",
"id": "my-training-quiz",
"category": "CO",
"title": "My Training — Knowledge Check",
"description": "Validate understanding of key concepts.",
"difficulty": "beginner",
"estimatedTime": 5,
"questions": [
{
"id": "q1",
"type": "multiple-choice",
"title": "Question title",
"content": "Question body text.",
"options": [
{ "id": "a", "text": "Correct answer", "isCorrect": true, "explanation": "Why it's correct." },
{ "id": "b", "text": "Wrong answer", "isCorrect": false, "explanation": "Why it's wrong." }
],
"correctAnswer": "a",
"explanation": "Full explanation shown after answering.",
"points": 1000,
"hints": ["First hint.", "Second hint."]
}
],
"maxScore": 1000,
"tags": ["my-training"],
"learningObjectives": ["State what the learner gains."]
}
Step 2 — Bind to a lesson page:
This template's example assessment is bound below. Click through it to see the learner experience:
8. Q&A / Knowledge Check (inline multiple-choice)#
Use when: you want a lightweight inline question without the overhead of a full assessment scenario. No separate JSON file needed.
<!-- LAB_QUESTION
type: multiple-choice
question: "What is the role of Orbital in the training runtime?"
options:
- "It provisions and runs the training container, exposing a PTY bridge for the interactive terminal"
- "It hosts the GitHub Pages site where the lesson markdown is served"
- "It is the Dynatrace app plugin that renders lesson content"
- "It manages the GitHub Actions workflow that deploys the training"
correct: 0
explanation: "Orbital is the execution backend. It runs the container, exposes the PTY terminal, and executes shell-verification commands on behalf of the Dynatrace app."
-->
All block types covered
You have seen every interactive block type in action. Continue to the example lesson to see them composed into a complete training.