- Split `scenarios/scenarios.json` into individual JSON files under `scenarios/data/` named `<scenario-id>.json` - Split `scenarios/bundles.json` into individual JSON files under `scenarios/bundles/` named `<bundle-id>.json` - Updated `backend/server.js` to dynamically load scenario and bundle files from their respective directories - Updated documentation in `scenarios/SCHEMA.md` and `README.md` to reflect the new repository layout and contributor workflow Signed-off-by: Abhinav Sinha <[email protected]>
214 lines
8.3 KiB
Markdown
214 lines
8.3 KiB
Markdown
# KubeKosh Configuration Schema Reference
|
|
|
|
KubeKosh uses two primary directories to define its curriculum, learning paths, and exam configurations:
|
|
1. **Bundles (`scenarios/bundles`)**: Each file defines one high-level study bundle (e.g., CKA, CKAD, CKS), including active highlights, exam duration, ordered list of included scenarios, and other fields.
|
|
2. **Scenarios (`scenarios/data`)**: Each file defines one individual exercise — a hands-on task or multiple-choice question (MCQ). Each scenario contains an environment setup script, cleanup script, hints, problem statement, automated validation script, and other fields.
|
|
|
|
---
|
|
|
|
## 1. Bundles Schema (`scenarios/bundles`)
|
|
|
|
Each file contains a **single bundle object**. The filename must match the bundle's `id` field (e.g., `k8s-basics.json`).
|
|
|
|
### Schema Fields
|
|
* **`id`** *(string, required)*: A unique, kebab-case identifier for the bundle (e.g., `k8s-basics`).
|
|
* **`name`** *(string, required)*: The human-readable name of the bundle shown in navigation (e.g., `Kubernetes Basics`).
|
|
* **`icon`** *(string, required)*: An emoji or glyph representing the bundle (e.g., `🌱`).
|
|
* **`tagline`** *(string, required)*: A short summary of the bundle's objectives.
|
|
* **`color`** *(string, required)*: Hex color code representing the bundle's UI identity/accent color (e.g., `#3fb950`).
|
|
* **`colorDim`** *(string, required)*: Translucent RGBA color matching the accent color at low opacity, used for UI row highlighting (e.g., `rgba(63,185,80,0.12)`).
|
|
* **`exam_minutes`** *(number, required)*: The time limit allocated for the mock exam in minutes (e.g., `60`).
|
|
* **`scenario_ids`** *(array of strings, required)*: List of scenario IDs belonging to this bundle in the order they should appear.
|
|
|
|
### Example Bundle
|
|
```json
|
|
{
|
|
"id": "k8s-basics",
|
|
"name": "Kubernetes Basics",
|
|
"icon": "🌱",
|
|
"tagline": "Core concepts for beginners",
|
|
"color": "#3fb950",
|
|
"colorDim": "rgba(63,185,80,0.12)",
|
|
"exam_minutes": 60,
|
|
"scenario_ids": [
|
|
"pod-basics-mcq",
|
|
"kubectl-essentials-mcq",
|
|
"namespaces-basics",
|
|
"deploy-nginx"
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Scenarios Schema (`scenarios/data`)
|
|
|
|
Each file contains a **single scenario object**. The filename must match the scenario's `id` field (e.g., `k8s-basics.json`).
|
|
|
|
Scenarios are defined as JSON objects. A scenario can be either a hands-on console challenge (`"task"`) or a multiple-choice question (`"mcq"`).
|
|
|
|
### Common Fields (All Types)
|
|
```jsonc
|
|
{
|
|
"id": "unique-kebab-case-id", // string — unique scenario identifier
|
|
"title": "Human-readable Title", // string — shown in sidebar list
|
|
"category": "Workloads", // string — groups scenarios in sidebar accordion
|
|
"difficulty": "Easy", // "Easy" | "Medium" | "Hard"
|
|
"type": "task", // "task" | "mcq"
|
|
"weight": 7, // number — points value (used for final grade scoring)
|
|
"description": "## Markdown...", // string — problem statement supporting GitHub-flavored Markdown
|
|
"hints": [...], // array — see Hints schema below
|
|
"setup_commands": [...], // array<object> — commands run on environment preparation
|
|
"teardown_commands": [...], // array<object> — optional — cleanup commands run after scenario completes
|
|
"default_namespace": "default" // string — optional — default active namespace for the terminal
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Hints Schema
|
|
Each hint is rendered as a collapsible card inside the Hints tab of the UI:
|
|
```jsonc
|
|
{
|
|
"title": "Short title for the hint card",
|
|
"body": "Explanation text (plain text, no markdown format).",
|
|
"command": "kubectl run nginx --image=nginx" // optional — renders a copyable code block
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Setup & Teardown Commands
|
|
* **`setup_commands`**: Executed sequentially on the Kubernetes cluster when the user starts a scenario or clicks **"Prepare Environment"**. Useful for pre-deploying resources or injecting bugs.
|
|
* **`teardown_commands`**: Optional cleanup commands run when moving away from or resetting a scenario.
|
|
* Commands must be **objects** with a `command` key:
|
|
```jsonc
|
|
"setup_commands": [
|
|
{ "command": "kubectl create namespace debug" },
|
|
{ "command": "kubectl create deployment broken-app --image=nginx:1.25 -n debug" }
|
|
]
|
|
```
|
|
* *Note:* Non-zero exit codes are tolerated (e.g., "namespace already exists" errors won't halt the pipeline). All commands execute as `root`.
|
|
|
|
---
|
|
|
|
### Type: `"task"` — Hands-On Scenario
|
|
Requires the user to run shell commands in the interactive terminal. The system runs an automated validation sequence to check the cluster state.
|
|
|
|
```jsonc
|
|
{
|
|
"type": "task",
|
|
"validation": {
|
|
"description": "Check that deployment has been correctly configured",
|
|
"commands": [
|
|
{
|
|
"description": "Checks the running pods count",
|
|
"command": "kubectl get deploy nginx -o jsonpath='{.status.readyReplicas}'",
|
|
"expected_output": "3",
|
|
"match": "exact" // "exact" | "contains" | "regex"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Match Modes
|
|
| Mode | Behaviour |
|
|
| :--- | :--- |
|
|
| `exact` | Trimmed stdout must exactly equal `expected_output`. |
|
|
| `contains` | stdout must contain `expected_output` as a substring. |
|
|
| `not_contains` | stdout must **not** contain `expected_output` as a substring. |
|
|
| `regex` | stdout must match the regular expression in `expected_output`. |
|
|
|
|
---
|
|
|
|
### Type: `"mcq"` — Multiple Choice Question
|
|
Renders a questionnaire block. No terminal is shown. The user answers by selecting an option.
|
|
|
|
```jsonc
|
|
{
|
|
"type": "mcq",
|
|
"options": [
|
|
{ "id": "a", "text": "Option A explanation" },
|
|
{ "id": "b", "text": "Option B explanation" },
|
|
{ "id": "c", "text": "Option C explanation" },
|
|
{ "id": "d", "text": "Option D explanation" }
|
|
],
|
|
"correct_option": "c", // must match one of the option IDs
|
|
"explanation": "Detailed explanation of why C is the correct answer." // shown after submitting
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Full Examples
|
|
|
|
### Full Example — Hands-On Task Scenario
|
|
```json
|
|
{
|
|
"id": "scale-deployment",
|
|
"title": "Scale a Deployment",
|
|
"category": "Workloads",
|
|
"difficulty": "Easy",
|
|
"type": "task",
|
|
"weight": 4,
|
|
"description": "## Scale the Deployment\n\nA deployment named `myapp` exists in the `default` namespace.\n\n**Scale it to 5 replicas.**",
|
|
"hints": [
|
|
{
|
|
"title": "Using kubectl scale",
|
|
"body": "The scale subcommand lets you change the replica count imperatively.",
|
|
"command": "kubectl scale deployment myapp --replicas=5"
|
|
}
|
|
],
|
|
"setup_commands": [
|
|
{ "command": "kubectl create deployment myapp --image=nginx:1.25 --replicas=1" }
|
|
],
|
|
"teardown_commands": [
|
|
{ "command": "kubectl delete deployment myapp --ignore-not-found" }
|
|
],
|
|
"default_namespace": "default",
|
|
"validation": {
|
|
"description": "Checks that myapp has 5 ready replicas.",
|
|
"commands": [
|
|
{
|
|
"description": "myapp has 5 ready replicas",
|
|
"command": "kubectl get deployment myapp -o jsonpath='{.status.readyReplicas}'",
|
|
"expected_output": "5",
|
|
"match": "exact"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Full Example — MCQ Scenario
|
|
```json
|
|
{
|
|
"id": "service-types-mcq",
|
|
"title": "Kubernetes Service Types",
|
|
"category": "Networking",
|
|
"difficulty": "Easy",
|
|
"type": "mcq",
|
|
"weight": 3,
|
|
"description": "## Kubernetes Service Types\n\nWhich `kubectl` command creates a ClusterIP service named `my-svc` exposing port 80 for a deployment named `my-app`?",
|
|
"options": [
|
|
{ "id": "a", "text": "kubectl expose deployment my-app --name=my-svc --port=80 --type=ClusterIP" },
|
|
{ "id": "b", "text": "kubectl create service my-svc --port=80" },
|
|
{ "id": "c", "text": "kubectl apply service my-app --port=80" },
|
|
{ "id": "d", "text": "kubectl expose pod my-app --name=my-svc --port=80 --type=NodePort" }
|
|
],
|
|
"correct_option": "a",
|
|
"explanation": "`kubectl expose deployment` is the correct imperative command. It creates a Service targeting the deployment's pods. `--type=ClusterIP` is the default but explicit here for clarity.",
|
|
"hints": [
|
|
{
|
|
"title": "kubectl expose syntax",
|
|
"body": "Use kubectl expose to create a Service from an existing resource. Specify the resource type, name, port, and service type.",
|
|
"command": "kubectl expose deployment my-app --name=my-svc --port=80 --type=ClusterIP"
|
|
}
|
|
],
|
|
"setup_commands": [],
|
|
"teardown_commands": [],
|
|
"default_namespace": "default"
|
|
}
|
|
```
|