From e007bcf000e7ee939fe7bec56d9cffc541bbb3f1 Mon Sep 17 00:00:00 2001 From: ced4568 Date: Wed, 26 Aug 2026 17:07:46 -0500 Subject: [PATCH] feat(root02): author Linux Administration & Networking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds all 6 ROOT 02 modules (Linux system administration, storage & filesystems, the Networking Mental Model, Linux network inspection, DNS & HTTP, Linux troubleshooting) — 37 activities, each with a real lesson, demonstration/guided exercise, knowledge check, hands-on Lab, and reflection. The Networking Mental Model module (M3) gets deliberately extra depth per its "critical module" designation, with no lab by design — conceptual mastery via knowledge check and guided exercise, matching root-05-m00's own precedent. Adds 6 new hands-on Linux/networking scenarios plus 2 MCQs: linux-service-inspection (inspects the platform's own real nginx without touching it — a genuinely safe, real service-inspection target that avoids ever risking the Lab's own connectivity), linux-service-lifecycle (start/ verify/stop a fully controllable service), linux-storage-inspection, linux-network-inspection (ip/ss/getent/curl, each framed as answering one specific diagnostic question), linux-dns-http-diagnosis (a genuine DNS-resolves-but-wrong-port failure, diagnosed and fixed), and linux-troubleshooting-framework (a real missing-config-file failure, resolved via the RootNode Observe/Evidence/Hypothesis/Test/Repair/Validate/Prevent framework). All tested end-to-end via the real scenario-validation API, including negative tests confirming they fail when unsolved. Wires rootnode-linux-networking-assessment (2 required + 4 random-draw scenarios) using the existing Phase 3B assessment engine. Co-Authored-By: Claude Sonnet 5 --- .../01-lesson-linux-system-administration.md | 64 +++++++++ ...demonstration-inspecting-a-real-service.md | 56 ++++++++ .../03-knowledge-check.md | 39 +++++ ...uided-exercise-planning-your-inspection.md | 32 +++++ .../05-reflection.md | 27 ++++ .../01-lesson-storage-and-filesystems.md | 50 +++++++ .../02-demonstration-df-vs-du.md | 53 +++++++ .../03-knowledge-check.md | 31 ++++ ...04-guided-exercise-narrowing-down-usage.md | 29 ++++ .../05-reflection.md | 27 ++++ .../01-lesson-networking-mental-model.md | 104 ++++++++++++++ ...-demonstration-three-failure-signatures.md | 56 ++++++++ .../03-knowledge-check.md | 45 ++++++ ...exercise-diagnosing-from-symptoms-alone.md | 33 +++++ .../05-reflection.md | 30 ++++ .../01-lesson-linux-network-inspection.md | 74 ++++++++++ ...onstration-five-commands-five-questions.md | 56 ++++++++ .../03-knowledge-check.md | 39 +++++ ...ded-exercise-choosing-the-right-command.md | 28 ++++ .../05-reflection.md | 25 ++++ .../01-lesson-dns-and-http.md | 58 ++++++++ .../02-demonstration-walking-the-full-path.md | 57 ++++++++ .../m05-dns-and-http/03-knowledge-check.md | 40 ++++++ ...ded-exercise-categorizing-real-failures.md | 32 +++++ .../m05-dns-and-http/05-reflection.md | 26 ++++ .../01-lesson-linux-troubleshooting.md | 63 ++++++++ ...02-demonstration-the-framework-in-order.md | 64 +++++++++ .../03-knowledge-check.md | 41 ++++++ ...-exercise-writing-your-hypothesis-first.md | 30 ++++ .../05-reflection.md | 31 ++++ ...root02_linux_administration_networking.sql | 136 ++++++++++++++++++ scenarios/data/linux-dns-http-diagnosis.json | 42 ++++++ scenarios/data/linux-network-inspection.json | 42 ++++++ scenarios/data/linux-service-inspection.json | 37 +++++ scenarios/data/linux-service-lifecycle.json | 37 +++++ scenarios/data/linux-service-state-mcq.json | 22 +++ scenarios/data/linux-storage-inspection.json | 37 +++++ .../data/linux-troubleshooting-framework.json | 37 +++++ .../data/linux-troubleshooting-order-mcq.json | 22 +++ 39 files changed, 1752 insertions(+) create mode 100644 content/root/root-02-linux-administration-networking/m01-linux-system-administration/01-lesson-linux-system-administration.md create mode 100644 content/root/root-02-linux-administration-networking/m01-linux-system-administration/02-demonstration-inspecting-a-real-service.md create mode 100644 content/root/root-02-linux-administration-networking/m01-linux-system-administration/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m01-linux-system-administration/04-guided-exercise-planning-your-inspection.md create mode 100644 content/root/root-02-linux-administration-networking/m01-linux-system-administration/05-reflection.md create mode 100644 content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/01-lesson-storage-and-filesystems.md create mode 100644 content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/02-demonstration-df-vs-du.md create mode 100644 content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/04-guided-exercise-narrowing-down-usage.md create mode 100644 content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/05-reflection.md create mode 100644 content/root/root-02-linux-administration-networking/m03-networking-mental-model/01-lesson-networking-mental-model.md create mode 100644 content/root/root-02-linux-administration-networking/m03-networking-mental-model/02-demonstration-three-failure-signatures.md create mode 100644 content/root/root-02-linux-administration-networking/m03-networking-mental-model/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m03-networking-mental-model/04-guided-exercise-diagnosing-from-symptoms-alone.md create mode 100644 content/root/root-02-linux-administration-networking/m03-networking-mental-model/05-reflection.md create mode 100644 content/root/root-02-linux-administration-networking/m04-linux-network-inspection/01-lesson-linux-network-inspection.md create mode 100644 content/root/root-02-linux-administration-networking/m04-linux-network-inspection/02-demonstration-five-commands-five-questions.md create mode 100644 content/root/root-02-linux-administration-networking/m04-linux-network-inspection/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m04-linux-network-inspection/04-guided-exercise-choosing-the-right-command.md create mode 100644 content/root/root-02-linux-administration-networking/m04-linux-network-inspection/05-reflection.md create mode 100644 content/root/root-02-linux-administration-networking/m05-dns-and-http/01-lesson-dns-and-http.md create mode 100644 content/root/root-02-linux-administration-networking/m05-dns-and-http/02-demonstration-walking-the-full-path.md create mode 100644 content/root/root-02-linux-administration-networking/m05-dns-and-http/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m05-dns-and-http/04-guided-exercise-categorizing-real-failures.md create mode 100644 content/root/root-02-linux-administration-networking/m05-dns-and-http/05-reflection.md create mode 100644 content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/01-lesson-linux-troubleshooting.md create mode 100644 content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/02-demonstration-the-framework-in-order.md create mode 100644 content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/03-knowledge-check.md create mode 100644 content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/04-guided-exercise-writing-your-hypothesis-first.md create mode 100644 content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/05-reflection.md create mode 100644 platform/api/migrations/026_phase6e_root02_linux_administration_networking.sql create mode 100644 scenarios/data/linux-dns-http-diagnosis.json create mode 100644 scenarios/data/linux-network-inspection.json create mode 100644 scenarios/data/linux-service-inspection.json create mode 100644 scenarios/data/linux-service-lifecycle.json create mode 100644 scenarios/data/linux-service-state-mcq.json create mode 100644 scenarios/data/linux-storage-inspection.json create mode 100644 scenarios/data/linux-troubleshooting-framework.json create mode 100644 scenarios/data/linux-troubleshooting-order-mcq.json diff --git a/content/root/root-02-linux-administration-networking/m01-linux-system-administration/01-lesson-linux-system-administration.md b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/01-lesson-linux-system-administration.md new file mode 100644 index 0000000..cb62350 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/01-lesson-linux-system-administration.md @@ -0,0 +1,64 @@ +# Linux System Administration + +## Five questions every administrator asks + +Whenever you're handed an unfamiliar running system, you're really always answering the same +five questions: **What is running? Who started it? Where is its configuration? Where are its +logs? How do I verify its state?** This module builds the vocabulary and tools for all five. + +## Packages: how software gets installed + +A **package manager** (`apt` on Debian/Ubuntu, `dnf`/`yum` on Fedora/RHEL) installs, updates, and +removes software along with tracking what depends on what — the alternative (manually downloading +and compiling everything) is exactly the kind of unreproducible mess package managers exist to +prevent. You won't install packages by hand often in this Lab, but recognizing `apt install` / +`apt list --installed` matters for reading real systems. + +## Services: long-running background programs + +A **service** is a program meant to run continuously in the background, outliving any single +terminal session — a web server, a database, a log collector. Unlike a command you run and wait +for, a service starts, keeps running, and is expected to still be there when you come back. + +## systemd and systemctl (the concept — not available in this Lab) + +On most real Linux servers, **systemd** is the process manager (PID 1) responsible for starting, +stopping, and supervising services, and `systemctl` is the command you'd use to control it +(`systemctl status nginx`, `systemctl restart nginx`, `journalctl -u nginx` for its logs). +**This disposable Lab container does not run systemd** — its PID 1 is a boot script that starts +k3s directly, the same way a real container almost never runs a full init system. This is worth +knowing honestly rather than pretending otherwise: the CONCEPTS below (a running process, its +config file, its logs, verifying its state) are identical whether the supervisor is systemd or +something else — this module's Labs teach those concepts against a service you CAN directly +inspect and control in this environment. + +## Configuration files and environment variables + +Most services read their settings from either a config file (a specific path, usually under +`/etc/`) or **environment variables** (`env` lists every variable in your current shell; +`export NAME=value` sets one). Knowing WHERE a service's configuration lives is exactly the +"where is its configuration" question from this lesson's opening — you'll answer it for real in +this module's first Lab. + +## Logs: where a service tells you what happened + +A well-behaved service writes what it's doing to a log — usually a file under `/var/log/`. +Reading logs (with `cat`, `tail`, `tail -f` — from ROOT 01) is almost always the first real step +in figuring out why something isn't working, which Module 6 builds into a full framework. + +## Instructor Notes + +**Lesson goal:** Establish the five-question mental model (running/started-by/config/logs/ +verify) as the organizing frame for the rest of this module, and be explicit and honest that +systemd isn't available in this specific Lab environment rather than silently working around it. + +**Approximate timing:** 15 minutes read; 10 minutes live. + +**Common misconception to watch for:** students who've used systemd elsewhere may expect +`systemctl` to exist here — address this directly and early rather than letting them discover +the error on their own. + +**Discussion question:** "If you SSH into an unfamiliar production server and need to figure out +what's running and whether it's healthy, what's the very first command you'd run?" (There's no +single right answer — the point is getting students to articulate their own process before this +module hands them formal tools for it.) diff --git a/content/root/root-02-linux-administration-networking/m01-linux-system-administration/02-demonstration-inspecting-a-real-service.md b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/02-demonstration-inspecting-a-real-service.md new file mode 100644 index 0000000..c27ff65 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/02-demonstration-inspecting-a-real-service.md @@ -0,0 +1,56 @@ +# Demonstration: Answering the Five Questions For Real + +## OBSERVE + +This Lab's own terminal is served by a real running service: nginx. + +``` +$ pgrep -x nginx +906 +``` + +Something with PID 906 is running. + +## HYPOTHESIS + +Predict: where would nginx's configuration file be, and where would its logs be? + +## INSPECT + +``` +$ cat /etc/nginx/nginx.conf | head -5 +worker_processes 1; +error_log /var/log/nginx/error.log warn; +pid /run/nginx.pid; +... +$ ls /var/log/nginx/ +access.log error.log +``` + +## CHANGE + +Confirm it's actually listening on the port you'd expect: + +``` +$ ss -lnt | grep :80 +LISTEN 0 511 0.0.0.0:80 0.0.0.0:* +``` + +## VERIFY + +All five questions answered with real evidence, not assumptions: it's running (PID 906), +listening on port 80, configured at `/etc/nginx/nginx.conf`, logging to `/var/log/nginx/`. This +exact sequence — process, port, config, logs — is what you'll repeat for almost any unfamiliar +service on any real system. + +## Instructor Notes + +**Demo goal:** Show the five-question checklist answered end-to-end against a real, already- +running service the students are themselves using right now — more convincing than an +abstract example. + +**Approximate timing:** 8 minutes live. + +**Common mistake to watch for:** stopping here at "PID exists" without also confirming it's +actually listening on the expected port — a process existing and a service actually being +reachable are two different facts, both worth checking. diff --git a/content/root/root-02-linux-administration-networking/m01-linux-system-administration/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/03-knowledge-check.md new file mode 100644 index 0000000..3e65658 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/03-knowledge-check.md @@ -0,0 +1,39 @@ +# Knowledge Check: Linux System Administration + +--- + +**1. What are the five questions this module opened with?** + +*Answer:* What is running? Who started it? Where is its configuration? Where are its logs? How +do I verify its state? + +--- + +**2. Why doesn't this Lab environment have `systemctl`?** + +*Answer:* This Lab's container doesn't run systemd as PID 1 — its boot script starts k3s +directly instead, the same way most containers don't run a full init system. The concepts +(inspecting a running service's process/config/logs/state) are the same regardless of which +supervisor manages it. + +--- + +**3. A process exists (`pgrep` finds a PID) but the service seems unreachable. What's the next +thing to check?** + +*Answer:* Whether it's actually listening on the expected port (`ss -lnt`) — a running process +and a reachable service are two separate facts; the process could have started but failed to +bind its port. + +--- + +**4. Where does a well-behaved Linux service typically write what it's doing?** + +*Answer:* A log file, usually under `/var/log/`. + +## Instructor Notes + +**Facilitation:** Question 2 is worth confirming explicitly if any student has prior systemd +experience — set the expectation before the Lab, not after a confusing error. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-02-linux-administration-networking/m01-linux-system-administration/04-guided-exercise-planning-your-inspection.md b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/04-guided-exercise-planning-your-inspection.md new file mode 100644 index 0000000..f4d4b27 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/04-guided-exercise-planning-your-inspection.md @@ -0,0 +1,32 @@ +# Guided Exercise: Plan Your Inspection Before You Type Anything + +This is a paper exercise — commit to a plan before running commands in the Lab. + +## Your task + +You're told "the payments service seems slow." Before touching a keyboard, write down, in +order, which of these five questions you'd check FIRST, SECOND, and so on, and the one command +you'd use for each: + +- Is it running at all? +- Is it listening on its expected port? +- What does its configuration say? +- What do its recent logs say? +- Has anything changed recently? + +## A worked shape to check yourself against + +A reasonable order: (1) is it running — `pgrep`/`ps`; (2) is it listening — `ss -lnt`; (3) recent +logs — `tail -n 50 ` (recent activity is usually more informative than config for a +sudden "slow" complaint); (4) configuration — only if logs point at a config-driven cause; (5) +recent changes — depends on context (deployment history, `ls -la` timestamps on config files). +There's no single correct order, but "running, then listening, then logs" before jumping to +config is a defensible default — you're checking the cheapest, fastest-answering questions +first. + +## Instructor Notes + +**Facilitation:** There's genuine room for disagreement on ordering here — the value is in +students defending their reasoning, not converging on one "correct" sequence. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m01-linux-system-administration/05-reflection.md b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/05-reflection.md new file mode 100644 index 0000000..f126f65 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m01-linux-system-administration/05-reflection.md @@ -0,0 +1,27 @@ +# Reflection: You Now Have a Repeatable Checklist + +## Reflection questions + +1. Before this module, if someone said "check whether the service is healthy," what would you + have actually done? How does that compare to the five-question checklist now? +2. You started and stopped a service you fully controlled this module. What's different about + doing the same thing to a service you DIDN'T start, on a machine you don't fully understand + yet? + +## Real Ops takeaway + +**The five-question checklist works on ANY unfamiliar Linux system, not just this Lab.** Running +process, listening port, config location, logs, and current state — this is genuinely how +experienced engineers approach a machine they've never touched before. You'll use this exact +checklist again in ROOT 04 when a container "doesn't seem to be working," and again in +Kubernetes when a Pod isn't behaving as expected — the tools change, the checklist doesn't. + +This module is complete. Continue to the next module. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 2 is worth real discussion time — the +honest answer is "you should be more careful and inspect more before acting," a good early +seed for the least-privilege/caution habits ROOT 09 formalizes much later. + +**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing. diff --git a/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/01-lesson-storage-and-filesystems.md b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/01-lesson-storage-and-filesystems.md new file mode 100644 index 0000000..90d3149 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/01-lesson-storage-and-filesystems.md @@ -0,0 +1,50 @@ +# Storage and Filesystems + +## Block devices and partitions, conceptually + +A **block device** is the kernel's view of a storage device (a disk, an SSD, a cloud volume) — +`lsblk` lists them. A device can be split into **partitions**, separate sections each formatted +with their own filesystem, though a single-partition disk is common too. You won't partition +anything in this Lab, but recognizing `lsblk`'s output (`sda`, `sda1`, `nvme0n1`...) matters for +reading real systems. + +## Filesystems and mount points + +A **filesystem** is the format that organizes raw blocks into the files/directories you actually +interact with (ext4, xfs, and — inside this container — overlay, a filesystem UNIQUE to how +containers layer their storage). A filesystem becomes usable by being **mounted** at some +directory path — `/` itself is a mount point, and additional filesystems can be mounted at other +paths (a separate disk mounted at `/data`, for example). + +## Persistent vs. temporary storage — a distinction that matters immediately + +Not everything written to disk is meant to last. `/tmp` is conventionally temporary (ROOT 01); +a container's own writable layer is, by convention and by ROOT 04's design, meant to be +DISPOSABLE — data written inside a container that isn't explicitly persisted elsewhere disappears +when that container is destroyed. This distinction — is this data supposed to survive, or not — +is the single most important storage question you'll carry forward into ROOT 04's volumes and +eventually Kubernetes' PersistentVolumes. + +## Measuring usage: `df` vs. `du` + +These answer two DIFFERENT questions, a common point of confusion: + +- **`df -h`** — how much space is used/free on an entire mounted FILESYSTEM. +- **`du -sh `** — how much space one specific DIRECTORY's contents actually use. + +A filesystem can show plenty of free space overall (`df`) while one specific directory has +unexpectedly ballooned (`du`) — you need both questions answered to diagnose "why is disk full." + +## Instructor Notes + +**Lesson goal:** Build the persistent-vs-temporary distinction explicitly, since it's the direct +conceptual root of ROOT 04's bind-mount/volume distinction and, later, Kubernetes storage. + +**Approximate timing:** 12 minutes read; 8 minutes live. + +**Common misconception to watch for:** conflating `df` and `du` — a student surprised that +`df` doesn't show them "which directory is huge" is a good moment to reinforce they answer +different questions. + +**Demo idea:** `df -h /` then `du -sh /var/log/*` side by side, narrating the different +question each answers. diff --git a/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/02-demonstration-df-vs-du.md b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/02-demonstration-df-vs-du.md new file mode 100644 index 0000000..493aa5b --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/02-demonstration-df-vs-du.md @@ -0,0 +1,53 @@ +# Demonstration: Two Different Questions, Two Different Commands + +## OBSERVE + +``` +$ df -h / +Filesystem Size Used Avail Use% Mounted on +overlay 937G 199G 691G 23% / +``` + +The root filesystem overall: 23% used, plenty free. + +## HYPOTHESIS + +Predict: does this tell you anything about which specific directory is using the most space? + +## INSPECT + +``` +$ du -sh /var/log/* 2>/dev/null +1.2M /var/log/nginx +850K /var/log/k3s.log +``` + +No — `df` only ever answers "how full is this filesystem overall." Finding a SPECIFIC large +consumer requires `du`, pointed at the directories you suspect. + +## CHANGE + +Narrow further into a single suspicious directory: + +``` +$ du -sh /var/log/nginx/* +600K /var/log/nginx/access.log +600K /var/log/nginx/error.log +``` + +## VERIFY + +You now know both the overall filesystem picture (`df`) AND exactly which files are contributing +to it (`du`, narrowed progressively) — two genuinely different tools for two genuinely different +questions, not interchangeable. + +## Instructor Notes + +**Demo goal:** Make the df/du distinction concrete by actually needing BOTH to answer a +realistic "what's using my disk" question. + +**Approximate timing:** 8 minutes live. + +**Common mistake to watch for:** running `du -sh /` directly (extremely slow on a large +filesystem, since it recurses everything) instead of narrowing progressively into suspected +directories first. diff --git a/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/03-knowledge-check.md new file mode 100644 index 0000000..2064c6b --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/03-knowledge-check.md @@ -0,0 +1,31 @@ +# Knowledge Check: Storage and Filesystems + +--- + +**1. `df -h /` shows only 23% used, but you suspect one directory is unusually large. Does +`df` alone answer that?** + +*Answer:* No — `df` reports overall filesystem usage, not per-directory. You need `du -sh ` +pointed at the suspect directory (or directories) to answer that. + +--- + +**2. What makes data in a container's writable layer different from data in `/home`?** + +*Answer:* By convention and design, a container's own writable layer is meant to be disposable — +data there disappears when the container is destroyed, unless explicitly persisted elsewhere +(a topic ROOT 04 covers directly). `/home` data is expected to persist normally. + +--- + +**3. What does "mounting" a filesystem actually mean?** + +*Answer:* Attaching a filesystem so it becomes accessible at a specific directory path — `/` +itself is a mount point, and additional filesystems can be mounted at other paths. + +## Instructor Notes + +**Facilitation:** Question 2 previews ROOT 04's volume/bind-mount content directly — worth +naming that connection explicitly. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/04-guided-exercise-narrowing-down-usage.md b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/04-guided-exercise-narrowing-down-usage.md new file mode 100644 index 0000000..aff85a3 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/04-guided-exercise-narrowing-down-usage.md @@ -0,0 +1,29 @@ +# Guided Exercise: Narrow Down a Disk Problem on Paper + +This is a paper exercise — commit to a plan before running commands in the Lab. + +## Your task + +`df -h /` shows the root filesystem is 95% full. You have three suspect directories: +`/var/log`, `/srv/cache`, and `/home`. Write the exact sequence of `du` commands you'd run to +find the actual culprit, narrowing your search efficiently rather than checking everything with +maximum depth immediately. + +## A worked shape to check yourself against + +``` +du -sh /var/log /srv/cache /home +``` + +— one command, comparing all three top-level suspects at once. Whichever is largest, THEN +narrow further into it specifically (e.g. `du -sh /srv/cache/*` if that one's the culprit) rather +than deep-diving into all three from the start — this mirrors Module 1's "cheapest, fastest- +answering question first" principle. + +## Instructor Notes + +**Facilitation:** Point out that running `du -sh` separately three times versus once with all +three paths produces the same information — the point isn't syntax, it's the "compare broadly, +then narrow" strategy. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/05-reflection.md b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/05-reflection.md new file mode 100644 index 0000000..784ed07 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/05-reflection.md @@ -0,0 +1,27 @@ +# Reflection: "It's Probably the Logs" Is a Guess, Not a Diagnosis + +## Reflection questions + +1. Before measuring, which of the three directories in this module's Lab did you guess would + be largest? Were you right? +2. Why does "persistent vs. temporary" matter enough to be worth memorizing as a question you + ask about EVERY piece of data, not just during a storage module? + +## Real Ops takeaway + +**Guessing which directory is consuming disk space, without measuring, is exactly the kind of +assumption that wastes real incident-response time.** `du`, applied progressively (broad, then +narrow), replaces guessing with evidence in seconds. And the persistent-vs-temporary question +you just practiced is the exact same question you'll ask again in ROOT 04 about container data, +and again much later about Kubernetes storage — a question worth internalizing now, cheaply, +rather than learning the hard way after data you assumed would persist didn't. + +This module is complete. Continue to the next module. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 1 works well as a quick poll before +revealing the real answer — the mismatch between guesses and reality reinforces the lesson +better than stating it directly. + +**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing. diff --git a/content/root/root-02-linux-administration-networking/m03-networking-mental-model/01-lesson-networking-mental-model.md b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/01-lesson-networking-mental-model.md new file mode 100644 index 0000000..144f839 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/01-lesson-networking-mental-model.md @@ -0,0 +1,104 @@ +# Networking Mental Model + +## Why this module gets extra attention + +Nearly everything you'll do for the rest of ROOT_ — container networking, Kubernetes Services, +Ingress, NetworkPolicy, DNS, cloud infrastructure — is a variation on the SAME small set of +ideas this module introduces. Get this model solid now, and every later networking topic becomes +"the same thing again, one layer up." Skip it, and every later module will feel like memorizing +unrelated facts. The goal here is operational REASONING, not subnet arithmetic — you will not be +asked to calculate a subnet mask by hand. + +## The chain, layer by layer + +Think of reaching another machine as passing through a fixed sequence of layers, each of which +can independently succeed or fail: + +``` +Host + ↓ +Network interface (this machine's connection to a network) + ↓ +IP address (this machine's address on that network) + ↓ +Subnet (which other addresses are "local" to reach directly) + ↓ +Default gateway (where to send anything NOT local) + ↓ +Routing (the actual path traffic takes) + ↓ +DNS (translating a name into an IP address) + ↓ +Port (which specific service on that IP) + ↓ +TCP / UDP (how the connection itself behaves) + ↓ +Application protocol (HTTP, etc. — the actual conversation) +``` + +## Each layer, briefly + +- **Network interface** — your machine's actual network connection (`eth0`, etc.). No interface, + no networking, full stop. +- **IP address** — this machine's address on a given network. IPv4 addresses look like + `172.17.0.2`. `127.0.0.1` (aka `localhost`) is special: it always means "this same machine," + never anywhere else. `0.0.0.0` is different again — when a SERVICE binds to it, it means + "listen on every interface this machine has," not a real destination address. +- **Subnet** — a range of addresses considered "local" to each other, expressed as CIDR + (`172.17.0.0/16` means every address starting `172.17.`). You don't need to calculate subnet + math by hand — you need to recognize that two addresses in the same subnet can talk directly, + while addresses outside it need a gateway. +- **Default gateway** — the address your machine sends traffic to when the destination ISN'T in + its own subnet — effectively "ask this router to figure out the rest." +- **Routing** — the actual decision-making about which path traffic takes; `ip route` shows your + machine's routing table (Module 4 makes this hands-on). +- **DNS** — translates a human-readable name (`example.com`) into an IP address, since IP + addresses aren't what humans want to remember or type. +- **Port** — a single IP address can host MANY services simultaneously; the port number is what + distinguishes "which one." Port 80 conventionally means HTTP, 443 HTTPS, 22 SSH — conventions, + not hard rules. +- **TCP vs. UDP** — TCP is connection-oriented and guarantees delivery/order (most web traffic, + SSH); UDP is connectionless and faster but delivers no guarantees (DNS queries themselves, + video streaming). Client/server: one side (the server) listens on a fixed port waiting for + connections; the other (the client) initiates them. + +## The reasoning skill: "where is the failure?" + +Given this chain, a real diagnostic question decomposes into a sequence of yes/no checks, each +isolating ONE layer: + +- Does the host have an IP address at all? +- Is the destination local (same subnet) or remote? +- Is there a valid route to it? +- Can the name resolve to an IP? +- Can the destination PORT actually be reached? +- Is a service actually listening there? +- Does the application itself respond correctly? + +Module 4 makes each of these a real command. Module 5 walks the complete request path end to +end. Module 6 formalizes this into RootNode's troubleshooting framework, applied to networking +specifically. + +## Instructor Notes + +**Lesson goal:** This module gets outsized weight for a reason — it's the single highest- +leverage conceptual investment in ROOT 02. Do not rush it to "cover the topics"; depth here pays +off across ROOT 04, ROOT 05-10, and beyond. + +**Approximate timing:** 25 minutes read; 15-20 minutes live with discussion — longer than a +typical lesson, deliberately. + +**Common misconception to watch for:** treating `0.0.0.0` as a real destination address rather +than a "listen on every interface" bind convention — this genuinely confuses students later when +they see it in a container's own listening-port output. + +**Discussion question:** Walk through a real failure narrative live: "You `curl` a URL and get +'Connection refused.' Which layers does that rule OUT, and which remain suspects?" (Answer: +rules out DNS entirely — you got FAR enough to attempt a TCP connection — and rules out "nothing +is listening at all" being the ENTIRE problem elsewhere; "refused" specifically means something +actively rejected the connection, most often nothing listening on that exact port.) + +**Optional extension:** contrast "Connection refused" vs. "Connection timed out" vs. "Could not +resolve host" as three DIFFERENT failure signatures pointing at different layers — worth a full +discussion pass if time allows, since Module 5 depends on students already sensing this +distinction. diff --git a/content/root/root-02-linux-administration-networking/m03-networking-mental-model/02-demonstration-three-failure-signatures.md b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/02-demonstration-three-failure-signatures.md new file mode 100644 index 0000000..60b54b7 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/02-demonstration-three-failure-signatures.md @@ -0,0 +1,56 @@ +# Demonstration: Three Failures That Sound Similar But Aren't + +## OBSERVE + +All three of these LOOK like "it didn't work" — but each one rules out and rules in completely +different layers. + +``` +$ curl http://this-name-does-not-resolve.invalid/ +curl: (6) Could not resolve host: this-name-does-not-resolve.invalid +``` + +## HYPOTHESIS + +Which layer does THIS failure point at — DNS, routing, the port, or the application itself? + +## INSPECT + +"Could not resolve host" happens BEFORE any network connection is even attempted — it's purely a +DNS failure. No IP was ever found to connect to. + +## CHANGE + +Now a different failure, against a name that DOES resolve: + +``` +$ curl http://localhost:9999/ +curl: (7) Failed to connect to localhost port 9999: Connection refused +``` + +DNS/name resolution already succeeded here (`localhost` always resolves). "Connection refused" +means a TCP packet reached the destination and was ACTIVELY rejected — almost always because +nothing is listening on that port. + +## VERIFY + +A third signature, distinct from both: + +``` +$ curl http://10.255.255.1:80/ +curl: (28) Failed to connect to 10.255.255.1 port 80: Connection timed out +``` + +No response at all, not even a rejection — usually means the destination is unreachable +(no route, or blocked silently), not that something is running but declining you. + +## Instructor Notes + +**Demo goal:** Get students to viscerally distinguish these three error MESSAGES as three +different diagnostic starting points, not interchangeable "it's broken" signals. + +**Approximate timing:** 10 minutes live. + +**Common mistake to watch for:** treating every connection failure as "the network is down" — +each of these three messages should immediately narrow which layer to investigate next, per +Module 5's troubleshooting exercise. diff --git a/content/root/root-02-linux-administration-networking/m03-networking-mental-model/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/03-knowledge-check.md new file mode 100644 index 0000000..5714b60 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/03-knowledge-check.md @@ -0,0 +1,45 @@ +# Knowledge Check: Networking Mental Model + +--- + +**1. What's the actual difference between an IP address and a port?** + +*Answer:* An IP address identifies a specific machine on a network; a port identifies WHICH +service on that machine, since one IP can host many services simultaneously. + +--- + +**2. `curl` reports "Could not resolve host." Which layer failed?** + +*Answer:* DNS — the name never got translated into an IP address, so no connection attempt was +even made. + +--- + +**3. `curl` reports "Connection refused" instead. What does that specifically tell you?** + +*Answer:* The name resolved fine and the destination was reachable, but nothing is listening on +the port you tried — the connection attempt was actively rejected, not silently ignored. + +--- + +**4. What is a default gateway for?** + +*Answer:* It's where your machine sends traffic whose destination ISN'T in its own local subnet +— effectively "ask this router to figure out how to get there." + +--- + +**5. True or false: `0.0.0.0` is a real destination address you could `curl`.** + +*Answer:* False (in the way most students initially assume) — when a SERVICE binds to +`0.0.0.0`, it means "listen on every interface," a bind convention, not a real host you'd +connect to as a destination. + +## Instructor Notes + +**Facilitation:** Questions 2-3 are worth spending real time on together — the point isn't +memorizing the exact error text, it's the underlying "which layer does this rule out" reasoning. + +**Approximate timing:** 8 minutes (slightly longer than a typical knowledge check, given this +module's weight). diff --git a/content/root/root-02-linux-administration-networking/m03-networking-mental-model/04-guided-exercise-diagnosing-from-symptoms-alone.md b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/04-guided-exercise-diagnosing-from-symptoms-alone.md new file mode 100644 index 0000000..8402c84 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/04-guided-exercise-diagnosing-from-symptoms-alone.md @@ -0,0 +1,33 @@ +# Guided Exercise: Diagnose From the Symptom Alone + +This is a paper exercise — reason through each case using only the mental model, before any +hands-on Lab work. + +## Your task + +For each symptom, name which layer(s) it rules OUT, and which remain genuine suspects: + +1. `ping 8.8.8.8` succeeds, but `curl https://example.com` fails with "Could not resolve host." +2. `dig example.com` returns a correct-looking IP address, but `curl https://example.com` times + out with no response at all. +3. `curl http://internal-service:8080/` returns "Connection refused," but + `curl http://internal-service:9090/` (a different port, same host) succeeds. + +## A worked shape to check yourself against + +1. Rules out: basic connectivity/routing to the internet (ping succeeded). Remaining suspect: + DNS specifically — something is preventing name resolution even though raw IP connectivity + works. +2. Rules out: DNS (resolution clearly succeeded). Remaining suspects: routing/reachability to + that specific IP, or a firewall silently dropping the connection — "no response at all" is the + timeout signature, not the "actively rejected" signature. +3. Rules out: DNS AND general reachability to that host (port 9090 worked). Remaining suspect: + narrowed all the way down to "nothing is listening on port 8080 specifically" — the most + precisely isolated diagnosis of the three. + +## Instructor Notes + +**Facilitation:** Item 3 is the strongest example of "isolate one variable" reasoning (same +host, different port) — worth highlighting explicitly as the gold-standard diagnostic pattern. + +**Approximate timing:** 10-12 minutes live discussion; 8 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m03-networking-mental-model/05-reflection.md b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/05-reflection.md new file mode 100644 index 0000000..905c00f --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m03-networking-mental-model/05-reflection.md @@ -0,0 +1,30 @@ +# Reflection: One Model, Reused Everywhere From Here On + +## Reflection questions + +1. Before this module, how would you have described "the network is broken" to a colleague? + How would you describe it now, more precisely? +2. This module deliberately avoided subnet math. Do you feel that cost you anything real, given + the module's actual goal (reasoning about WHERE a failure is, not calculating addresses)? + +## Real Ops takeaway + +**Every networking topic for the rest of ROOT_ is this same chain, viewed from a different +altitude.** A container's published port is still "port," a Kubernetes Service is still "how do +I reach the right destination via a stable address," Ingress is still "routing decisions," and +NetworkPolicy is still "which subnets/ports are allowed to reach which." You are not about to +learn a new mental model in ROOT 04 or ROOT 05 — you're about to see this SAME one applied one +layer up, repeatedly. That's why this module got extra weight: the investment compounds. + +This module is complete. Continue to Module 4, where these ideas become real commands. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 1 is worth collecting several answers +before and after — the change in precision is itself the evidence this module worked. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing. + +**Milestone note:** this is the "critical module" this course was built around — consider +confirming as a group that everyone can walk the full chain (host→interface→IP→subnet→gateway→ +routing→DNS→port→TCP/UDP→application) from memory before moving on. diff --git a/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/01-lesson-linux-network-inspection.md b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/01-lesson-linux-network-inspection.md new file mode 100644 index 0000000..867e312 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/01-lesson-linux-network-inspection.md @@ -0,0 +1,74 @@ +# Linux Network Inspection + +## Every command below is really a question + +Module 3 gave you the model. This module gives you the commands — but treat each one as +ANSWERING a specific question, not as syntax to memorize. + +## "What addresses does this machine have?" — `ip addr` + +``` +$ ip -4 addr show eth0 +inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 +``` + +This machine's IPv4 address is `172.17.0.2`, on a `/16` subnet. + +## "Where will this machine send traffic?" — `ip route` + +``` +$ ip route +default via 172.17.0.1 dev eth0 +172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2 +``` + +The second line says: anything in `172.17.0.0/16` is reachable directly. The first line (the +`default` route) says: everything else goes through `172.17.0.1` — the default gateway from +Module 3, now a real line in a real table. + +## "What TCP ports are listening?" — `ss -lnt` + +``` +$ ss -lnt +LISTEN 0 511 0.0.0.0:80 0.0.0.0:* +``` + +Something is listening on port 80, on every interface (`0.0.0.0` as a BIND address, exactly the +convention Module 3 flagged). `ss` replaced the older `netstat` on modern Linux — same idea. + +## "What does DNS say this name resolves to?" — `getent hosts` / `dig` + +``` +$ getent hosts example.test +10.0.0.5 example.test +``` + +`getent hosts` checks the same resolution path the rest of the system uses (local `/etc/hosts` +entries first, then DNS) — a reliable first choice. `dig ` queries DNS directly and shows +more detail, useful once you specifically suspect a real DNS server issue rather than a local +override. + +## "Can the application actually respond?" — `curl` + +``` +$ curl -sf http://localhost:80/ -o /dev/null -w '%{http_code}\n' +200 +``` + +Everything before this point (address, route, port, DNS) can be perfect, and the APPLICATION +itself can still be broken — `curl` is the only one of these tools that actually tests the full +chain end to end, including the application's own response. + +## Instructor Notes + +**Lesson goal:** Convert Module 3's mental model into muscle-memory commands, each explicitly +framed as answering one specific diagnostic question. + +**Approximate timing:** 15 minutes read; 10 minutes live. + +**Common misconception to watch for:** treating all five commands as redundant ways to "check +the network" — reinforce that each answers a DIFFERENT layer's question, and a real diagnosis +usually needs more than one. + +**Demo idea:** run all five in sequence against this Lab's own nginx, narrating which Module 3 +layer each one answers. diff --git a/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/02-demonstration-five-commands-five-questions.md b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/02-demonstration-five-commands-five-questions.md new file mode 100644 index 0000000..04fcbf4 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/02-demonstration-five-commands-five-questions.md @@ -0,0 +1,56 @@ +# Demonstration: The Same Five Questions, Answered Live + +## OBSERVE + +``` +$ ip -4 addr show eth0 +inet 172.17.0.2/16 ... +``` + +"What address do I have?" — answered. + +## HYPOTHESIS + +Predict what `ip route` will show before running it — specifically, predict whether there will +be a `default` line. + +## INSPECT + +``` +$ ip route +default via 172.17.0.1 dev eth0 +172.17.0.0/16 dev eth0 ... +``` + +Confirmed — a `default` route exists, meaning traffic to anywhere outside `172.17.0.0/16` has +somewhere to go. + +## CHANGE + +``` +$ ss -lnt +LISTEN 0 511 0.0.0.0:80 0.0.0.0:* +``` + +"What's listening?" — port 80, confirmed. + +## VERIFY + +``` +$ curl -sf http://localhost/ -o /dev/null -w '%{http_code}\n' +200 +``` + +The full chain, verified end to end with real evidence at each layer — not "the network seems +fine," but five specific, falsifiable answers. + +## Instructor Notes + +**Demo goal:** Chain all five commands together as ONE continuous diagnostic narrative, not +five disconnected syntax examples. + +**Approximate timing:** 8 minutes live. + +**Common mistake to watch for:** running `curl` first and stopping at "it worked" without ever +having separately confirmed address/route/port — fine when things work, but the WRONG habit for +when they don't (Module 5's Lab requires the full sequence). diff --git a/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/03-knowledge-check.md new file mode 100644 index 0000000..800bb10 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/03-knowledge-check.md @@ -0,0 +1,39 @@ +# Knowledge Check: Linux Network Inspection + +--- + +**1. Which command answers "what TCP ports are listening on this machine"?** + +*Answer:* `ss -lnt`. + +--- + +**2. Which command answers "where will this machine send traffic that isn't local"?** + +*Answer:* `ip route` — specifically, the `default` line in its output. + +--- + +**3. Why is `curl` the only tool in this module that can confirm the APPLICATION itself is +working, not just the network path to it?** + +*Answer:* Address, route, port, and DNS can all be perfectly correct while the application +behind them still returns an error or wrong content — `curl` is the only one of these tools that +actually completes an HTTP request and inspects the real response. + +--- + +**4. `getent hosts` and `dig` both resolve names. When would you reach for `dig` specifically +instead of `getent hosts`?** + +*Answer:* When you specifically suspect a DNS server issue and want to query DNS directly with +more detail — `getent hosts` checks the same path the rest of the system uses (including local +`/etc/hosts` overrides first), which is usually the right first choice, but doesn't show you as +much about what the actual DNS server itself said. + +## Instructor Notes + +**Facilitation:** Question 3 reinforces Module 3's "layers can each independently succeed or +fail" idea with a very concrete example. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/04-guided-exercise-choosing-the-right-command.md b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/04-guided-exercise-choosing-the-right-command.md new file mode 100644 index 0000000..886f6f8 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/04-guided-exercise-choosing-the-right-command.md @@ -0,0 +1,28 @@ +# Guided Exercise: Which Command Answers This Question? + +This is a paper exercise — commit to an answer before checking it in the Lab. + +## Your task + +For each question, name the ONE command you'd reach for first: + +1. "Does this machine have a working IP address at all?" +2. "Is anything listening on port 8080 right now?" +3. "Does `payments.internal` resolve to an address?" +4. "Is the web server actually returning a valid response, not just accepting connections?" +5. "Where does traffic to an address outside my local subnet actually go?" + +## A worked shape to check yourself against + +1. `ip -4 addr show` +2. `ss -lnt | grep 8080` +3. `getent hosts payments.internal` +4. `curl -sf http://.../ -o /dev/null -w '%{http_code}'` +5. `ip route` + +## Instructor Notes + +**Facilitation:** Have students justify WHY their chosen command answers the specific question +asked, not just that it's "a networking command" — the goal is precision, not familiarity. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/05-reflection.md b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/05-reflection.md new file mode 100644 index 0000000..3845e8e --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m04-linux-network-inspection/05-reflection.md @@ -0,0 +1,25 @@ +# Reflection: Five Commands, Five Answers, One Diagnosis + +## Reflection questions + +1. Which of these five commands did you already know before this module? Which was + genuinely new? +2. You now have a real toolkit for Module 3's "where is the failure" question. What's still + missing before you'd feel fully confident diagnosing a real production networking issue? + +## Real Ops takeaway + +**Five specific commands, each answering one specific question, beat one vague instinct that +"something's wrong with the network."** This is the exact toolkit real engineers reach for +first, in this order, when handed an unfamiliar connectivity problem. Module 5 puts all five +into one continuous diagnostic story; Module 6 formalizes the discipline around using them. + +This module is complete. Continue to the next module. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 2's honest answer is usually +"experience with real, varied failures" — a good moment to set expectations that fluency +compounds with practice, not from one module. + +**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing. diff --git a/content/root/root-02-linux-administration-networking/m05-dns-and-http/01-lesson-dns-and-http.md b/content/root/root-02-linux-administration-networking/m05-dns-and-http/01-lesson-dns-and-http.md new file mode 100644 index 0000000..1aa49a3 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m05-dns-and-http/01-lesson-dns-and-http.md @@ -0,0 +1,58 @@ +# DNS and HTTP + +## The complete request path + +Every time a browser or `curl` fetches a URL, it walks the entire Module 3 chain in order: + +``` +Application (curl / browser) + ↓ +DNS lookup — translate the name to an IP + ↓ +IP address obtained + ↓ +Routing — figure out the path to that IP + ↓ +TCP connection established — a three-way handshake, underneath curl + ↓ +HTTP request sent + ↓ +Server processes it + ↓ +HTTP response returned +``` + +Nothing here is new — this is Module 3's chain and Module 4's commands, just walked start to +finish as ONE continuous story instead of five separate checks. + +## Four distinct failure categories, not one + +Given this path, "it doesn't work" always decomposes into exactly one of four categories: + +1. **DNS failure** — the name never resolved to an IP at all (`curl`'s "Could not resolve + host"). +2. **Routing/connection failure** — the IP was found, but no TCP connection could be established + — either refused (something's there but nothing's listening) or timed out (no response at + all, usually unreachable or silently blocked). +3. **TCP succeeded, but the application never responds correctly** — a connection was + established, but the server hangs, drops the connection, or the request never completes. +4. **HTTP/application failure** — a full HTTP response comes back, but it's WRONG (a 500 error, + a 404, unexpected content) — everything below the application layer worked fine. + +These are genuinely different problems requiring genuinely different fixes — conflating them +(treating a 404 the same as a DNS failure) wastes real diagnostic time. + +## Instructor Notes + +**Lesson goal:** Make the four-category failure taxonomy explicit and distinct — this is the +direct foundation for this module's diagnostic Lab and for Module 6's formal framework. + +**Approximate timing:** 15 minutes read; 10 minutes live. + +**Common misconception to watch for:** treating an HTTP 500 error the same category as "the +network is down" — a 500 means the ENTIRE network path worked; the failure is purely in the +application's own logic. + +**Discussion question:** "A request returns HTTP 404. Which of the four categories is that, and +what does it rule out?" (Answer: category 4, application/HTTP — it rules out DNS, routing, AND +TCP connectivity entirely, since a real HTTP response came back at all.) diff --git a/content/root/root-02-linux-administration-networking/m05-dns-and-http/02-demonstration-walking-the-full-path.md b/content/root/root-02-linux-administration-networking/m05-dns-and-http/02-demonstration-walking-the-full-path.md new file mode 100644 index 0000000..bf525f9 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m05-dns-and-http/02-demonstration-walking-the-full-path.md @@ -0,0 +1,57 @@ +# Demonstration: Walking the Complete Path, Layer by Layer + +## OBSERVE + +``` +$ getent hosts lab-internal.test +10.99.99.42 lab-internal.test +``` + +DNS: succeeded. + +## HYPOTHESIS + +Predict: if the next step (TCP connection) fails, what would that specific error look like? + +## INSPECT + +``` +$ curl http://lab-internal.test:9091/ +curl: (7) Failed to connect to lab-internal.test port 9091: Connection refused +``` + +"Connection refused" — DNS already succeeded (ruled out), the failure is specifically that +nothing is listening on port 9091 at that address. + +## CHANGE + +Find what IS actually listening, on the correct address: + +``` +$ ss -lnt | grep LISTEN +LISTEN 0 5 0.0.0.0:9095 0.0.0.0:* +``` + +The real service is on 9095, not 9091 — a configuration/expectation mismatch, not a DNS or +routing problem. + +## VERIFY + +``` +$ curl http://lab-internal.test:9095/ +app running +``` + +The full path, once pointed at the CORRECT layer that was actually broken (the port, not DNS), +succeeds end to end. + +## Instructor Notes + +**Demo goal:** Show that a real, plausible-sounding complaint ("DNS must be broken") is +disproven by evidence in seconds, and the REAL cause is one layer downstream. + +**Approximate timing:** 10 minutes live. + +**Common mistake to watch for:** jumping straight to "restart everything" instead of walking +the chain layer by layer to find the SPECIFIC broken step — the whole point of this module's +Lab is resisting that shortcut. diff --git a/content/root/root-02-linux-administration-networking/m05-dns-and-http/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m05-dns-and-http/03-knowledge-check.md new file mode 100644 index 0000000..66f7a78 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m05-dns-and-http/03-knowledge-check.md @@ -0,0 +1,40 @@ +# Knowledge Check: DNS and HTTP + +--- + +**1. Name the four failure categories this module introduced.** + +*Answer:* DNS failure, routing/connection failure, TCP-succeeds-but-application-never-responds, +and HTTP/application failure (a full response comes back but it's wrong). + +--- + +**2. A request returns HTTP 500. What does that tell you about DNS and TCP connectivity?** + +*Answer:* Both worked completely — a 500 means a full HTTP response was received, so the failure +is purely inside the application's own logic, not the network path to reach it. + +--- + +**3. Why is "connection refused" a MORE specific diagnosis than "timed out"?** + +*Answer:* "Refused" means a packet reached the destination and was actively rejected (almost +always: nothing listening on that port) — you know the destination is reachable. "Timed out" +means no response at all, which could mean unreachable, no route, or silently blocked — +genuinely less specific. + +--- + +**4. A teammate says "DNS must be broken" because a request fails. What should you check +BEFORE agreeing?** + +*Answer:* Whether the name actually resolves at all (`getent hosts` or `dig`) — many failures +that "feel like DNS" are actually one layer downstream (routing, port, or application), and +DNS itself may be working perfectly. + +## Instructor Notes + +**Facilitation:** Question 4 mirrors this module's own Lab almost exactly — a good moment to +preview it if not already started. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-02-linux-administration-networking/m05-dns-and-http/04-guided-exercise-categorizing-real-failures.md b/content/root/root-02-linux-administration-networking/m05-dns-and-http/04-guided-exercise-categorizing-real-failures.md new file mode 100644 index 0000000..34c382b --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m05-dns-and-http/04-guided-exercise-categorizing-real-failures.md @@ -0,0 +1,32 @@ +# Guided Exercise: Categorize the Failure Before You Fix It + +This is a paper exercise — commit to a category before checking it in the Lab. + +## Your task + +For each symptom, name which of the four failure categories (DNS / routing-connection / +TCP-hangs / HTTP-application) it belongs to: + +1. `curl: (6) Could not resolve host: api.internal` +2. `curl: (7) Failed to connect to api.internal port 443: Connection refused` +3. A request eventually returns, but with HTTP 502 Bad Gateway +4. A request just hangs for 30 seconds with no response, then finally times out + +## A worked shape to check yourself against + +1. DNS — the name never resolved. +2. Routing/connection — DNS worked, but nothing accepted the connection on that port. +3. HTTP/application — a full HTTP response came back (502 is a real, complete response), so + DNS/routing/TCP all worked; the failure is in the application layer (often a proxy unable to + reach ITS OWN backend, which is itself worth investigating further). +4. TCP-hangs / ambiguous-timeout — this is the least specific signature; it could mean + unreachable, silently dropped by a firewall, or an application that accepted the connection + but never responds — worth narrowing further with `ss`/`ip route` before concluding. + +## Instructor Notes + +**Facilitation:** Item 3 (502) is a good one to slow down on — many students initially assume +any 5xx code means "the network is broken," when it actually proves the OPPOSITE for every +layer below the application. + +**Approximate timing:** 10-12 minutes live discussion; 8 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m05-dns-and-http/05-reflection.md b/content/root/root-02-linux-administration-networking/m05-dns-and-http/05-reflection.md new file mode 100644 index 0000000..2286c26 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m05-dns-and-http/05-reflection.md @@ -0,0 +1,26 @@ +# Reflection: You Fixed the Real Problem, Not the Suspected One + +## Reflection questions + +1. Before running any commands in this module's Lab, what would you have guessed was broken? + Was your guess right? +2. Why does correctly categorizing a failure (DNS vs. routing vs. TCP vs. application) matter + more than just "trying things until something works"? + +## Real Ops takeaway + +**"DNS must be broken" is one of the most common wrong first guesses in real incident response +— and disproving or confirming it takes seconds, not minutes.** The habit this module builds — +categorize the failure precisely before attempting a fix — is what separates a five-minute +diagnosis from an hour of trying unrelated things. This exact reasoning, applied to Kubernetes +Services/Ingress/DNS later, is one of the most valuable transferable skills in this entire +program. + +This module is complete. Continue to the final module of this course. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 1 works well as a live poll before +revealing the Lab's actual root cause — the gap between instinct and evidence is the lesson. + +**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing. diff --git a/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/01-lesson-linux-troubleshooting.md b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/01-lesson-linux-troubleshooting.md new file mode 100644 index 0000000..d546c9b --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/01-lesson-linux-troubleshooting.md @@ -0,0 +1,63 @@ +# Linux Troubleshooting + +## The RootNode troubleshooting framework + +Every module in ROOT 02 has been building toward this: a repeatable, disciplined process for +diagnosing ANY Linux-level failure, not just the specific ones you've already seen. + +``` +OBSERVE + ↓ +GATHER EVIDENCE + ↓ +FORM HYPOTHESIS + ↓ +TEST + ↓ +REPAIR + ↓ +VALIDATE + ↓ +PREVENT +``` + +## Each step, and why skipping it costs you later + +- **Observe** — what's the actual, specific symptom? Not "it's broken" — the exact error, the + exact command that failed. +- **Gather evidence** — logs, process state, listening ports, config content — the raw facts, + collected BEFORE guessing at a cause. +- **Form hypothesis** — based on the evidence (not a guess made before looking), what's the most + likely cause? +- **Test** — check whether your hypothesis is actually true, ideally without yet making changes + that could mask the real problem. +- **Repair** — make the minimal change that addresses the CONFIRMED cause, not a broad "try + everything" fix. +- **Validate** — confirm the original symptom is actually gone, with real evidence, not + assumption. +- **Prevent recurrence** — what would stop this from happening again? Skipping this step is how + teams end up fixing the exact same incident repeatedly. + +## Why "skip straight to fixing" costs you + +Jumping straight from OBSERVE to REPAIR (skipping evidence and hypothesis) means you're guessing +— sometimes you'll get lucky, but you won't have LEARNED anything transferable, and you risk +"fixing" a symptom while leaving the actual cause untouched, guaranteeing a repeat. Every command +from Modules 1, 2, and 4 IS the "gather evidence" step, made concrete. + +## Instructor Notes + +**Lesson goal:** Consolidate every command this course taught into ONE disciplined process, +directly matching the identical framework `root-10-m03-troubleshooting-framework` teaches at the +Kubernetes level — introduced here first, at the Linux level, so it already feels familiar by +then. + +**Approximate timing:** 15 minutes read; 10 minutes live. + +**Common misconception to watch for:** treating this as a rigid checklist to recite rather than +an actual habit — the value only shows up when it's applied under a real, unfamiliar failure +(this module's Lab). + +**Discussion question:** "Describe a time you fixed something without understanding WHY it +broke. Did it come back?" (Real personal examples land this lesson better than abstract +description.) diff --git a/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/02-demonstration-the-framework-in-order.md b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/02-demonstration-the-framework-in-order.md new file mode 100644 index 0000000..ff22c48 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/02-demonstration-the-framework-in-order.md @@ -0,0 +1,64 @@ +# Demonstration: Every Step, In Order, On a Real Failure + +## OBSERVE + +A startup script fails: `/srv/demo/start.sh` exits with an error. That's the specific, concrete +symptom — not "the service is down." + +## GATHER EVIDENCE + +``` +$ /srv/demo/start.sh +FATAL: config file not found: /srv/demo/config/settings.conf +``` + +The script's own error message IS the evidence — no guessing required. + +## FORM HYPOTHESIS + +The config file at that exact path doesn't exist. That's the specific, testable hypothesis — +not "something's misconfigured." + +## TEST + +``` +$ ls /srv/demo/config/ +ls: cannot access '/srv/demo/config/': No such file or directory +``` + +Confirmed — the directory itself doesn't exist yet, not just the file. + +## REPAIR + +``` +$ mkdir -p /srv/demo/config +$ echo "mode=production" > /srv/demo/config/settings.conf +``` + +The minimal fix for the CONFIRMED cause — not a broader "reinstall everything" overcorrection. + +## VALIDATE + +``` +$ /srv/demo/start.sh +Started with config: mode=production +``` + +The original symptom is gone, confirmed with real output, not assumption. + +## PREVENT RECURRENCE + +A real prevention step here: the script should probably check for and create its config +directory on first run, or the deployment process should ensure the config exists before the +service ever starts — naming this explicitly is what separates a real fix from a lucky one. + +## Instructor Notes + +**Demo goal:** Show all seven steps applied in strict order to one concrete, realistic failure +— the exact structure this module's Lab requires students to repeat themselves. + +**Approximate timing:** 10 minutes live. + +**Common mistake to watch for:** skipping straight to REPAIR (creating the file) without first +articulating the hypothesis and testing it — even when the fix seems obvious, naming the +hypothesis explicitly is the habit this module is building. diff --git a/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/03-knowledge-check.md b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/03-knowledge-check.md new file mode 100644 index 0000000..e90996a --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/03-knowledge-check.md @@ -0,0 +1,41 @@ +# Knowledge Check: Linux Troubleshooting + +--- + +**1. What's the difference between OBSERVE and GATHER EVIDENCE?** + +*Answer:* OBSERVE names the specific symptom (what actually failed, precisely). GATHER EVIDENCE +collects the raw facts (logs, config, process/port state) needed to understand WHY, before +guessing at a cause. + +--- + +**2. Why does FORM HYPOTHESIS come before TEST, rather than jumping straight to REPAIR?** + +*Answer:* A hypothesis is a specific, falsifiable guess at the cause based on evidence already +gathered — testing it BEFORE repairing confirms you're about to fix the right thing, rather than +making a change that might not address the actual cause at all. + +--- + +**3. A fix works, but the same failure happens again a week later. Which framework step was +most likely skipped?** + +*Answer:* PREVENT RECURRENCE — the immediate symptom was resolved, but whatever allowed the +underlying cause to occur in the first place was never addressed. + +--- + +**4. Why is "repair the minimal confirmed cause" better than "try several things until it +works"?** + +*Answer:* A minimal, confirmed repair means you actually know what fixed it and can prevent +recurrence deliberately. Trying multiple things at once means even success leaves you unable to +say WHICH change mattered — and unable to reliably prevent the same failure next time. + +## Instructor Notes + +**Facilitation:** Question 3 is a good real-world check-in — ask if anyone has a personal +example of a fix that didn't actually stick. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/04-guided-exercise-writing-your-hypothesis-first.md b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/04-guided-exercise-writing-your-hypothesis-first.md new file mode 100644 index 0000000..95dfa13 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/04-guided-exercise-writing-your-hypothesis-first.md @@ -0,0 +1,30 @@ +# Guided Exercise: Write Your Hypothesis Before You Look at the Answer + +This is a paper exercise — commit to a full framework pass before touching the Lab. + +## Your task + +You're told: "A script that's supposed to write a `ready` marker file isn't creating it, and +exits with a non-zero code." Before running anything, write out, specifically: + +1. What's your OBSERVE statement (the precise symptom, not "it's broken")? +2. What EVIDENCE would you gather first, and with what command? +3. What's ONE specific, testable hypothesis (not "something's wrong with it")? + +## A worked shape to check yourself against + +1. Observe: "Script X exits non-zero and does not create file Y." +2. Evidence: redirect both stdout and stderr to a file and read it — + `script.sh > log 2>&1; cat log` — the script's own error output is almost always the fastest + evidence source for a startup failure. +3. Hypothesis (once you've actually read the error): something SPECIFIC the error names — + e.g. "the script requires a file at path X that doesn't currently exist" — not a vague + restatement of the symptom. + +## Instructor Notes + +**Facilitation:** The value here is entirely in getting students to write a hypothesis +SPECIFIC and FALSIFIABLE enough that they could be proven wrong — "something's misconfigured" +fails that bar; "the config file at this exact path is missing" passes it. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced. diff --git a/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/05-reflection.md b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/05-reflection.md new file mode 100644 index 0000000..590b795 --- /dev/null +++ b/content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/05-reflection.md @@ -0,0 +1,31 @@ +# Reflection: This Is the Same Framework You'll Use for the Rest of ROOT_ + +## Reflection questions + +1. Which framework step did you personally find easiest to skip or rush through in this + module's Lab? Why do you think that step specifically tempts shortcuts? +2. You've now used this exact seven-step framework — Observe, Gather Evidence, Form Hypothesis, + Test, Repair, Validate, Prevent Recurrence — at the Linux level. Where do you expect to use + it again? + +## Real Ops takeaway + +**This entire course, from Module 1's five-question service checklist through Module 5's +failure-category taxonomy, was building toward this one repeatable discipline.** You'll meet +this exact framework again, formally named, in ROOT 10 — applied to Kubernetes instead of raw +Linux. Having already practiced it here means ROOT 10 won't be teaching you a new skill, just +retargeting a skill you already have. + +This module — and ROOT 02 — is complete. Continue to ROOT 03: Git & Collaborative Development. + +## Instructor Notes + +**Discussion questions for a live classroom:** question 1 tends to surface "Prevent Recurrence" +and "Form Hypothesis" (jumping straight to a guessed fix) as the two most commonly admitted +shortcuts — worth normalizing as a real, common temptation rather than a personal failing. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing. + +**Milestone note:** this is the last module of ROOT 02 — consider a short recap connecting all +six modules (services → storage → networking model → inspection tools → DNS/HTTP → framework) +before moving to ROOT 03. diff --git a/platform/api/migrations/026_phase6e_root02_linux_administration_networking.sql b/platform/api/migrations/026_phase6e_root02_linux_administration_networking.sql new file mode 100644 index 0000000..3bb6b25 --- /dev/null +++ b/platform/api/migrations/026_phase6e_root02_linux_administration_networking.sql @@ -0,0 +1,136 @@ +-- Phase 6E — ROOT 02: Linux Administration & Networking. Adds 6 modules/activities under the +-- existing (PLANNED, zero-content) root-02-linux-administration-networking course row. Every +-- lab activity references a NEW scenario file using the existing generic scenario/validation +-- engine (docs/ROOT_FOUNDATIONS_LAB_STRATEGY.md §1) — no new mechanism. Module 3 (Networking +-- Mental Model) has no lab activity by design — a conceptual module earns READY through +-- demonstrated reasoning (its knowledge check and guided exercise), matching root-05-m00's own +-- precedent — not a forced lab. + +UPDATE courses SET + status = 'ready', + objectives = '{"Answer what is running/who started it/where is its config/where are its logs for a real service","Distinguish persistent from temporary storage and measure real disk usage","Explain the host-to-application networking chain and reason about where a failure lives","Use ip/ss/getent/curl to answer specific diagnostic questions","Categorize a request failure as DNS, routing, TCP, or application","Apply the RootNode troubleshooting framework to a real Linux failure"}', + updated_at = now() +WHERE course_id = 'root-02-linux-administration-networking'; + +INSERT INTO course_modules (module_id, course_id, title, description, sequence, objectives, prerequisites, estimated_duration_minutes, status) VALUES +('root-02-m01-linux-system-administration', 'root-02-linux-administration-networking', 'Linux System Administration', + 'The five-question service checklist (running/started-by/config/logs/verify), applied to a real running service. Target learner: has completed ROOT 01.', + 1, '{"Inspect a real service''s process, config, and logs","Start, verify, and stop a service you control","Explain why this Lab has no systemd, and why the concepts transfer anyway"}', '{}', 45, 'ready'), +('root-02-m02-storage-and-filesystems', 'root-02-linux-administration-networking', 'Storage & Filesystems', + 'Block devices, mounts, and the persistent-vs-temporary distinction that carries into ROOT 04''s volumes and Kubernetes storage.', + 2, '{"Distinguish df from du and use both correctly","Explain persistent vs. temporary storage","Find the real consumer of disk space in a directory tree"}', '{root-02-m01-linux-system-administration}', 40, 'ready'), +('root-02-m03-networking-mental-model', 'root-02-linux-administration-networking', 'Networking Mental Model', + 'THE critical module of ROOT 02: the host-to-application networking chain, and operational reasoning about where a failure lives. No lab — conceptual mastery via demonstrated reasoning.', + 3, '{"Walk the full host-interface-IP-subnet-gateway-routing-DNS-port-TCP/UDP-application chain","Distinguish DNS, routing, TCP, and application failure signatures","Explain 0.0.0.0 and 127.0.0.1 correctly"}', '{root-02-m02-storage-and-filesystems}', 55, 'ready'), +('root-02-m04-linux-network-inspection', 'root-02-linux-administration-networking', 'Linux Network Inspection', + 'The Networking Mental Model made hands-on: ip, ss, getent, curl, each framed as answering one specific question.', + 4, '{"Use ip addr/ip route to answer address and routing questions","Use ss to find listening ports","Use getent hosts and curl to test resolution and application reachability"}', '{root-02-m03-networking-mental-model}', 45, 'ready'), +('root-02-m05-dns-and-http', 'root-02-linux-administration-networking', 'DNS & HTTP', + 'The complete request path end to end, and the four-category failure taxonomy (DNS/routing/TCP/application).', + 5, '{"Walk the complete DNS-to-HTTP-response request path","Categorize a failure as DNS, routing/connection, TCP, or application","Diagnose and fix a real port-mismatch failure"}', '{root-02-m04-linux-network-inspection}', 45, 'ready'), +('root-02-m06-linux-troubleshooting', 'root-02-linux-administration-networking', 'Linux Troubleshooting', + 'The RootNode troubleshooting framework (Observe/Evidence/Hypothesis/Test/Repair/Validate/Prevent), applied to a real broken-startup-script failure.', + 6, '{"Apply the 7-step troubleshooting framework in strict order","Diagnose a real failure from its own error output","Name a real prevention step, not just a fix"}', '{root-02-m05-dns-and-http}', 45, 'ready') +ON CONFLICT (module_id) DO NOTHING; + +INSERT INTO module_activities (activity_id, module_id, sequence, activity_type, title, content_path, scenario_id, assessment_definition_id, completion_behavior, estimated_duration_minutes, status) VALUES +-- M01 (2 labs) +('root-02-m01-a01-lesson', 'root-02-m01-linux-system-administration', 1, 'lesson', 'Linux System Administration', + 'content/root/root-02-linux-administration-networking/m01-linux-system-administration/01-lesson-linux-system-administration.md', NULL, NULL, 'manual_marker', 15, 'ready'), +('root-02-m01-a02-demonstration', 'root-02-m01-linux-system-administration', 2, 'demonstration', 'Demonstration: Answering the Five Questions For Real', + 'content/root/root-02-linux-administration-networking/m01-linux-system-administration/02-demonstration-inspecting-a-real-service.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m01-a03-knowledge-check', 'root-02-m01-linux-system-administration', 3, 'knowledge_check', 'Knowledge Check: Linux System Administration', + 'content/root/root-02-linux-administration-networking/m01-linux-system-administration/03-knowledge-check.md', NULL, NULL, 'manual_marker', 5, 'ready'), +('root-02-m01-a04-guided-exercise', 'root-02-m01-linux-system-administration', 4, 'guided_exercise', 'Guided Exercise: Plan Your Inspection Before You Type Anything', + 'content/root/root-02-linux-administration-networking/m01-linux-system-administration/04-guided-exercise-planning-your-inspection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m01-a05-lab-service-inspection', 'root-02-m01-linux-system-administration', 5, 'lab', 'Lab: Inspect a Real Running Service', + NULL, 'linux-service-inspection', NULL, 'auto_scenario', 10, 'ready'), +('root-02-m01-a06-lab-service-lifecycle', 'root-02-m01-linux-system-administration', 6, 'lab', 'Lab: Start, Verify, and Stop a Service', + NULL, 'linux-service-lifecycle', NULL, 'auto_scenario', 10, 'ready'), +('root-02-m01-a07-reflection', 'root-02-m01-linux-system-administration', 7, 'reflection', 'Reflection: You Now Have a Repeatable Checklist', + 'content/root/root-02-linux-administration-networking/m01-linux-system-administration/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +-- M02 +('root-02-m02-a01-lesson', 'root-02-m02-storage-and-filesystems', 1, 'lesson', 'Storage and Filesystems', + 'content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/01-lesson-storage-and-filesystems.md', NULL, NULL, 'manual_marker', 12, 'ready'), +('root-02-m02-a02-demonstration', 'root-02-m02-storage-and-filesystems', 2, 'demonstration', 'Demonstration: Two Different Questions, Two Different Commands', + 'content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/02-demonstration-df-vs-du.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m02-a03-knowledge-check', 'root-02-m02-storage-and-filesystems', 3, 'knowledge_check', 'Knowledge Check: Storage and Filesystems', + 'content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/03-knowledge-check.md', NULL, NULL, 'manual_marker', 5, 'ready'), +('root-02-m02-a04-guided-exercise', 'root-02-m02-storage-and-filesystems', 4, 'guided_exercise', 'Guided Exercise: Narrow Down a Disk Problem on Paper', + 'content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/04-guided-exercise-narrowing-down-usage.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m02-a05-lab-storage-inspection', 'root-02-m02-storage-and-filesystems', 5, 'lab', 'Lab: Find What''s Actually Using the Disk', + NULL, 'linux-storage-inspection', NULL, 'auto_scenario', 10, 'ready'), +('root-02-m02-a06-reflection', 'root-02-m02-storage-and-filesystems', 6, 'reflection', 'Reflection: "It''s Probably the Logs" Is a Guess, Not a Diagnosis', + 'content/root/root-02-linux-administration-networking/m02-storage-and-filesystems/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +-- M03 (no lab) +('root-02-m03-a01-lesson', 'root-02-m03-networking-mental-model', 1, 'lesson', 'Networking Mental Model', + 'content/root/root-02-linux-administration-networking/m03-networking-mental-model/01-lesson-networking-mental-model.md', NULL, NULL, 'manual_marker', 25, 'ready'), +('root-02-m03-a02-demonstration', 'root-02-m03-networking-mental-model', 2, 'demonstration', 'Demonstration: Three Failures That Sound Similar But Aren''t', + 'content/root/root-02-linux-administration-networking/m03-networking-mental-model/02-demonstration-three-failure-signatures.md', NULL, NULL, 'manual_marker', 10, 'ready'), +('root-02-m03-a03-knowledge-check', 'root-02-m03-networking-mental-model', 3, 'knowledge_check', 'Knowledge Check: Networking Mental Model', + 'content/root/root-02-linux-administration-networking/m03-networking-mental-model/03-knowledge-check.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m03-a04-guided-exercise', 'root-02-m03-networking-mental-model', 4, 'guided_exercise', 'Guided Exercise: Diagnose From the Symptom Alone', + 'content/root/root-02-linux-administration-networking/m03-networking-mental-model/04-guided-exercise-diagnosing-from-symptoms-alone.md', NULL, NULL, 'manual_marker', 12, 'ready'), +('root-02-m03-a05-reflection', 'root-02-m03-networking-mental-model', 5, 'reflection', 'Reflection: One Model, Reused Everywhere From Here On', + 'content/root/root-02-linux-administration-networking/m03-networking-mental-model/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +-- M04 +('root-02-m04-a01-lesson', 'root-02-m04-linux-network-inspection', 1, 'lesson', 'Linux Network Inspection', + 'content/root/root-02-linux-administration-networking/m04-linux-network-inspection/01-lesson-linux-network-inspection.md', NULL, NULL, 'manual_marker', 15, 'ready'), +('root-02-m04-a02-demonstration', 'root-02-m04-linux-network-inspection', 2, 'demonstration', 'Demonstration: The Same Five Questions, Answered Live', + 'content/root/root-02-linux-administration-networking/m04-linux-network-inspection/02-demonstration-five-commands-five-questions.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m04-a03-knowledge-check', 'root-02-m04-linux-network-inspection', 3, 'knowledge_check', 'Knowledge Check: Linux Network Inspection', + 'content/root/root-02-linux-administration-networking/m04-linux-network-inspection/03-knowledge-check.md', NULL, NULL, 'manual_marker', 5, 'ready'), +('root-02-m04-a04-guided-exercise', 'root-02-m04-linux-network-inspection', 4, 'guided_exercise', 'Guided Exercise: Which Command Answers This Question?', + 'content/root/root-02-linux-administration-networking/m04-linux-network-inspection/04-guided-exercise-choosing-the-right-command.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m04-a05-lab-network-inspection', 'root-02-m04-linux-network-inspection', 5, 'lab', 'Lab: Answer Three Real Questions With Three Real Commands', + NULL, 'linux-network-inspection', NULL, 'auto_scenario', 10, 'ready'), +('root-02-m04-a06-reflection', 'root-02-m04-linux-network-inspection', 6, 'reflection', 'Reflection: Five Commands, Five Answers, One Diagnosis', + 'content/root/root-02-linux-administration-networking/m04-linux-network-inspection/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +-- M05 +('root-02-m05-a01-lesson', 'root-02-m05-dns-and-http', 1, 'lesson', 'DNS and HTTP', + 'content/root/root-02-linux-administration-networking/m05-dns-and-http/01-lesson-dns-and-http.md', NULL, NULL, 'manual_marker', 15, 'ready'), +('root-02-m05-a02-demonstration', 'root-02-m05-dns-and-http', 2, 'demonstration', 'Demonstration: Walking the Complete Path, Layer by Layer', + 'content/root/root-02-linux-administration-networking/m05-dns-and-http/02-demonstration-walking-the-full-path.md', NULL, NULL, 'manual_marker', 8, 'ready'), +('root-02-m05-a03-knowledge-check', 'root-02-m05-dns-and-http', 3, 'knowledge_check', 'Knowledge Check: DNS and HTTP', + 'content/root/root-02-linux-administration-networking/m05-dns-and-http/03-knowledge-check.md', NULL, NULL, 'manual_marker', 5, 'ready'), +('root-02-m05-a04-guided-exercise', 'root-02-m05-dns-and-http', 4, 'guided_exercise', 'Guided Exercise: Categorize the Failure Before You Fix It', + 'content/root/root-02-linux-administration-networking/m05-dns-and-http/04-guided-exercise-categorizing-real-failures.md', NULL, NULL, 'manual_marker', 10, 'ready'), +('root-02-m05-a05-lab-dns-http-diagnosis', 'root-02-m05-dns-and-http', 5, 'lab', 'Lab: DNS Resolves, But the Connection Still Fails — Why?', + NULL, 'linux-dns-http-diagnosis', NULL, 'auto_scenario', 12, 'ready'), +('root-02-m05-a06-reflection', 'root-02-m05-dns-and-http', 6, 'reflection', 'Reflection: You Fixed the Real Problem, Not the Suspected One', + 'content/root/root-02-linux-administration-networking/m05-dns-and-http/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready'), +-- M06 +('root-02-m06-a01-lesson', 'root-02-m06-linux-troubleshooting', 1, 'lesson', 'Linux Troubleshooting', + 'content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/01-lesson-linux-troubleshooting.md', NULL, NULL, 'manual_marker', 15, 'ready'), +('root-02-m06-a02-demonstration', 'root-02-m06-linux-troubleshooting', 2, 'demonstration', 'Demonstration: Every Step, In Order, On a Real Failure', + 'content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/02-demonstration-the-framework-in-order.md', NULL, NULL, 'manual_marker', 10, 'ready'), +('root-02-m06-a03-knowledge-check', 'root-02-m06-linux-troubleshooting', 3, 'knowledge_check', 'Knowledge Check: Linux Troubleshooting', + 'content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/03-knowledge-check.md', NULL, NULL, 'manual_marker', 5, 'ready'), +('root-02-m06-a04-guided-exercise', 'root-02-m06-linux-troubleshooting', 4, 'guided_exercise', 'Guided Exercise: Write Your Hypothesis Before You Look at the Answer', + 'content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/04-guided-exercise-writing-your-hypothesis-first.md', NULL, NULL, 'manual_marker', 10, 'ready'), +('root-02-m06-a05-lab-troubleshooting-framework', 'root-02-m06-linux-troubleshooting', 5, 'lab', 'Lab: A Service Won''t Start — Apply the Framework, Don''t Just Guess', + NULL, 'linux-troubleshooting-framework', NULL, 'auto_scenario', 15, 'ready'), +('root-02-m06-a06-reflection', 'root-02-m06-linux-troubleshooting', 6, 'reflection', 'Reflection: This Is the Same Framework You''ll Use for the Rest of ROOT_', + 'content/root/root-02-linux-administration-networking/m06-linux-troubleshooting/05-reflection.md', NULL, NULL, 'manual_marker', 8, 'ready') +ON CONFLICT (activity_id) DO NOTHING; + +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +-- ROOT 02 course assessment — mixes real state-validation tasks with knowledge-recall MCQs +-- reasoning about service state and failure-layer diagnosis, via the existing Phase 3B engine. +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +INSERT INTO assessment_definitions (assessment_definition_id, type, title, description, duration_minutes, hint_policy, pool_spec, completion_rule) +VALUES ( + 'rootnode-linux-networking-assessment', 'exam', 'ROOT 02 Linux Administration & Networking Assessment', + 'A timed check across service administration, storage, networking diagnosis, and the troubleshooting framework — combining real state-validation tasks with conceptual reasoning questions.', + 35, 'disabled', + '{"required": ["linux-network-inspection", "linux-troubleshooting-framework"], + "random": [ + {"count":2,"from":["linux-service-inspection","linux-service-lifecycle","linux-storage-inspection","linux-dns-http-diagnosis"]}, + {"count":2,"from":["linux-troubleshooting-order-mcq","linux-service-state-mcq"]} + ]}', + '{"type":"all_scenarios_passed"}' +) ON CONFLICT (assessment_definition_id) DO NOTHING; + +INSERT INTO module_activities (activity_id, module_id, sequence, activity_type, title, content_path, scenario_id, assessment_definition_id, completion_behavior, estimated_duration_minutes, status) VALUES +('root-02-m06-a07-assessment', 'root-02-m06-linux-troubleshooting', 7, 'assessment', 'ROOT 02 Linux Administration & Networking Assessment', NULL, NULL, 'rootnode-linux-networking-assessment', 'auto_assessment', 35, 'ready') +ON CONFLICT (activity_id) DO NOTHING; diff --git a/scenarios/data/linux-dns-http-diagnosis.json b/scenarios/data/linux-dns-http-diagnosis.json new file mode 100644 index 0000000..db76cf0 --- /dev/null +++ b/scenarios/data/linux-dns-http-diagnosis.json @@ -0,0 +1,42 @@ +{ + "id": "linux-dns-http-diagnosis", + "title": "DNS Resolves, But the Connection Still Fails — Why?", + "category": "Networking", + "isolation_type": "shared_namespace", + "difficulty": "Medium", + "type": "task", + "weight": 6, + "description": "## DNS Resolves, But the Connection Still Fails — Why?\n\nAn application is supposed to be reachable at `http://broken-app.test:9091/`. A teammate insists \"DNS must be broken.\" Prove or disprove that, then actually fix the problem.\n\n**Your task:**\n\n1. Confirm whether `broken-app.test` actually resolves (`getent hosts broken-app.test`) — record your finding\n2. Try `curl http://broken-app.test:9091/` and observe what actually happens\n3. Find out what's REALLY running and on which port (hint: something already answers on a different port)\n4. Fix it so `curl http://broken-app.test:9091/` succeeds with a 200 response\n\nDo not just change the app's advertised port in documentation — make the SERVICE actually listen on 9091.", + "hints": [ + { + "title": "Separate DNS from connectivity", + "body": "`getent hosts broken-app.test` only tells you whether the NAME resolves — it says nothing about whether anything is listening on the port you're trying to reach. Test them as two separate questions.", + "command": "getent hosts broken-app.test" + }, + { + "title": "Finding what's actually running", + "body": "`ss -lnt` lists every port something is actually listening on right now — compare that against 9091.", + "command": "ss -lnt" + }, + { + "title": "The fix", + "body": "Stop whatever's running on the wrong port, then start the real service correctly on 9091.", + "command": "kill $(cat /srv/dns-http-lab/wrong.pid)\npython3 -m http.server 9091 --directory /srv/dns-http-lab &\necho $! > /srv/dns-http-lab/fixed.pid" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/dns-http-lab && echo 'app running' > /srv/dns-http-lab/index.html" }, + { "command": "grep -q broken-app.test /etc/hosts || echo '127.0.0.1 broken-app.test' >> /etc/hosts" }, + { "command": "python3 -m http.server 9095 --directory /srv/dns-http-lab >/srv/dns-http-lab/wrong.log 2>&1 & echo $! > /srv/dns-http-lab/wrong.pid" } + ], + "validation": { + "commands": [ + { "description": "broken-app.test genuinely resolves (DNS was never the actual problem)", "command": "getent hosts broken-app.test | awk '{print $1}'", "expected_output": "127.0.0.1", "match": "exact" }, + { "description": "the service now actually answers on port 9091", "command": "curl -sf http://broken-app.test:9091/ -o /dev/null -w '%{http_code}'", "expected_output": "200", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "[ -f /srv/dns-http-lab/wrong.pid ] && kill $(cat /srv/dns-http-lab/wrong.pid) 2>/dev/null; [ -f /srv/dns-http-lab/fixed.pid ] && kill $(cat /srv/dns-http-lab/fixed.pid) 2>/dev/null; rm -rf /srv/dns-http-lab" } + ] +} diff --git a/scenarios/data/linux-network-inspection.json b/scenarios/data/linux-network-inspection.json new file mode 100644 index 0000000..e5c5ad7 --- /dev/null +++ b/scenarios/data/linux-network-inspection.json @@ -0,0 +1,42 @@ +{ + "id": "linux-network-inspection", + "title": "Answer Three Real Questions With Three Real Commands", + "category": "Networking", + "isolation_type": "shared_namespace", + "difficulty": "Easy", + "type": "task", + "weight": 4, + "description": "## Answer Three Real Questions With Three Real Commands\n\nEach networking command answers a specific question. Practice treating them that way.\n\n**Your task:**\n\n1. \"What IP address does this machine have?\" — write your `eth0` IPv4 address to `/srv/network-lab/my-ip.txt`\n2. \"What's listening on port 80?\" — confirm something is, and write `80` to `/srv/network-lab/listening-port.txt`\n3. \"What does DNS say `lab-internal.test` resolves to?\" — a hosts entry for it already exists on this machine; write the resolved IP to `/srv/network-lab/resolved-ip.txt`", + "hints": [ + { + "title": "Your own IP", + "body": "`ip -4 addr show eth0` prints your interface's IPv4 address in an `inet a.b.c.d/nn` line.", + "command": "ip -4 addr show eth0" + }, + { + "title": "What's listening", + "body": "`ss -lnt` lists every TCP socket in LISTEN state and its port.", + "command": "ss -lnt" + }, + { + "title": "Resolving a name", + "body": "`getent hosts ` resolves a hostname using the same lookup mechanism the rest of the system uses (checks /etc/hosts, then DNS) — a reliable choice when you're not sure which one will answer.", + "command": "getent hosts lab-internal.test" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/network-lab" }, + { "command": "grep -q lab-internal.test /etc/hosts || echo '10.99.99.42 lab-internal.test' >> /etc/hosts" } + ], + "validation": { + "commands": [ + { "description": "correctly identified a real IPv4 address", "command": "cat /srv/network-lab/my-ip.txt", "expected_output": "^([0-9]{1,3}\\.){3}[0-9]{1,3}$", "match": "regex" }, + { "description": "correctly identified port 80 as listening", "command": "cat /srv/network-lab/listening-port.txt", "expected_output": "80", "match": "exact" }, + { "description": "correctly resolved lab-internal.test", "command": "cat /srv/network-lab/resolved-ip.txt", "expected_output": "10.99.99.42", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "rm -rf /srv/network-lab" } + ] +} diff --git a/scenarios/data/linux-service-inspection.json b/scenarios/data/linux-service-inspection.json new file mode 100644 index 0000000..ba9cf21 --- /dev/null +++ b/scenarios/data/linux-service-inspection.json @@ -0,0 +1,37 @@ +{ + "id": "linux-service-inspection", + "title": "Inspect a Real Running Service", + "category": "Linux Administration", + "isolation_type": "shared_namespace", + "difficulty": "Easy", + "type": "task", + "weight": 4, + "description": "## Inspect a Real Running Service\n\nThis Lab environment is itself served by a real running service: nginx, reverse-proxying the very terminal you're using right now. Answer the four questions every service-administration task starts with, using ONLY inspection commands (do not stop or restart it).\n\n**Your task:**\n\nCreate `/srv/service-lab/report.txt` with exactly three lines:\n\n```\nLISTENING_PORT=\nCONFIG_PATH=\nLOG_DIR=\n```", + "hints": [ + { + "title": "Finding the listening port", + "body": "`ss -lnt` lists every TCP socket in LISTEN state and the port it's bound to.", + "command": "ss -lnt" + }, + { + "title": "Finding the config and logs", + "body": "nginx's config path and log directives are visible right in its own config file.", + "command": "cat /etc/nginx/nginx.conf | head -10" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/service-lab" } + ], + "validation": { + "commands": [ + { "description": "correctly identified the listening port (80)", "command": "cat /srv/service-lab/report.txt | grep '^LISTENING_PORT='", "expected_output": "LISTENING_PORT=80", "match": "exact" }, + { "description": "correctly identified the config path", "command": "cat /srv/service-lab/report.txt | grep '^CONFIG_PATH='", "expected_output": "CONFIG_PATH=/etc/nginx/nginx.conf", "match": "exact" }, + { "description": "correctly identified the log directory", "command": "cat /srv/service-lab/report.txt | grep '^LOG_DIR='", "expected_output": "LOG_DIR=/var/log/nginx", "match": "exact" }, + { "description": "nginx itself was never stopped during this exercise", "command": "pgrep -x nginx >/dev/null && echo still-running || echo stopped", "expected_output": "still-running", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "rm -rf /srv/service-lab" } + ] +} diff --git a/scenarios/data/linux-service-lifecycle.json b/scenarios/data/linux-service-lifecycle.json new file mode 100644 index 0000000..a96b417 --- /dev/null +++ b/scenarios/data/linux-service-lifecycle.json @@ -0,0 +1,37 @@ +{ + "id": "linux-service-lifecycle", + "title": "Start, Verify, and Stop a Service", + "category": "Linux Administration", + "isolation_type": "shared_namespace", + "difficulty": "Easy", + "type": "task", + "weight": 4, + "description": "## Start, Verify, and Stop a Service\n\nUnlike the previous exercise (inspecting a service you must never touch), this one is yours to fully control.\n\n**Your task:**\n\n1. Start a small HTTP service in the background, listening on port 8890: `python3 -m http.server 8890 --directory /srv/service-lab &`, and save its PID to `/srv/service-lab/service.pid`\n2. Confirm it's actually listening, and record the result to `/srv/service-lab/status-before.txt` (write exactly `listening`)\n3. Stop it using the PID you saved\n4. Confirm it's actually gone, and record the result to `/srv/service-lab/status-after.txt` (write exactly `stopped`)", + "hints": [ + { + "title": "Starting and recording the PID", + "body": "`$!` after backgrounding a command holds its PID.", + "command": "mkdir -p /srv/service-lab\npython3 -m http.server 8890 --directory /srv/service-lab >/srv/service-lab/service.log 2>&1 &\necho $! > /srv/service-lab/service.pid" + }, + { + "title": "Verifying and stopping", + "body": "`ss -lnt | grep 8890` confirms it's listening. `kill $(cat .../service.pid)` stops it using the PID you recorded, not a hardcoded number.", + "command": "ss -lnt | grep 8890 && echo listening > /srv/service-lab/status-before.txt\nkill $(cat /srv/service-lab/service.pid)\nsleep 1\nss -lnt | grep 8890 || echo stopped > /srv/service-lab/status-after.txt" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/service-lab" } + ], + "validation": { + "commands": [ + { "description": "confirmed the service was actually listening before stopping it", "command": "cat /srv/service-lab/status-before.txt", "expected_output": "listening", "match": "exact" }, + { "description": "confirmed the service is actually stopped now", "command": "cat /srv/service-lab/status-after.txt", "expected_output": "stopped", "match": "exact" }, + { "description": "the process is genuinely no longer running", "command": "kill -0 $(cat /srv/service-lab/service.pid) 2>/dev/null && echo RUNNING || echo GONE", "expected_output": "GONE", "match": "exact" }, + { "description": "port 8890 is genuinely no longer bound", "command": "ss -lnt | grep -q 8890 && echo BOUND || echo FREE", "expected_output": "FREE", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "[ -f /srv/service-lab/service.pid ] && kill $(cat /srv/service-lab/service.pid) 2>/dev/null; rm -rf /srv/service-lab" } + ] +} diff --git a/scenarios/data/linux-service-state-mcq.json b/scenarios/data/linux-service-state-mcq.json new file mode 100644 index 0000000..9ad9c54 --- /dev/null +++ b/scenarios/data/linux-service-state-mcq.json @@ -0,0 +1,22 @@ +{ + "id": "linux-service-state-mcq", + "title": "Verifying a Service Is Actually Running", + "category": "Linux Administration", + "isolation_type": "shared_namespace", + "difficulty": "Easy", + "type": "mcq", + "weight": 3, + "description": "## Verifying a Service Is Actually Running\n\nA colleague says \"I restarted the service, it should be fine now.\" What's the single most reliable way to actually confirm the service is both running AND reachable on its expected port, without asking them?", + "options": [ + { "id": "a", "text": "Trust their statement — restarting almost always works" }, + { "id": "b", "text": "Check that its process exists (e.g. via ps/pgrep) AND that its expected port is in LISTEN state (e.g. via ss) — a running process alone doesn't guarantee it bound its port successfully" }, + { "id": "c", "text": "Check the service's source code for bugs" }, + { "id": "d", "text": "Wait 24 hours and see if anyone complains" } + ], + "correct_option": "b", + "explanation": "A process can be running (visible in ps) while having failed to bind its expected port (e.g. it's already in use, or it crashed after startup logging but before binding) — checking both facts independently is what actually confirms \"running and reachable,\" not either one alone.", + "hints": [], + "setup_commands": [], + "default_namespace": "default", + "teardown_commands": [] +} diff --git a/scenarios/data/linux-storage-inspection.json b/scenarios/data/linux-storage-inspection.json new file mode 100644 index 0000000..5ee946b --- /dev/null +++ b/scenarios/data/linux-storage-inspection.json @@ -0,0 +1,37 @@ +{ + "id": "linux-storage-inspection", + "title": "Find What's Actually Using the Disk", + "category": "Linux Administration", + "isolation_type": "shared_namespace", + "difficulty": "Easy", + "type": "task", + "weight": 4, + "description": "## Find What's Actually Using the Disk\n\nThree directories under `/srv/storage-lab/` (`logs`, `cache`, `data`) contain different amounts of data. \"It's probably the logs\" is a guess — find the real answer.\n\n**Your task:**\n\n1. Use `du` to measure the actual size of each of the three directories\n2. Use `df` to check overall filesystem usage on `/`\n3. Write the name of the directory (just `logs`, `cache`, or `data`) that uses the MOST space to `/srv/storage-lab/biggest.txt`", + "hints": [ + { + "title": "Measuring each directory", + "body": "`du -sh ` summarizes total size for one directory. Run it against all three, or use a wildcard to see them side by side.", + "command": "du -sh /srv/storage-lab/*/" + }, + { + "title": "Overall filesystem usage", + "body": "`df -h /` shows total/used/available space for the filesystem root lives on — a different, complementary question from `du` (which measures a specific directory's contents).", + "command": "df -h /" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/storage-lab/logs /srv/storage-lab/cache /srv/storage-lab/data" }, + { "command": "dd if=/dev/zero of=/srv/storage-lab/logs/app.log bs=1M count=1 2>/dev/null" }, + { "command": "dd if=/dev/zero of=/srv/storage-lab/cache/blob.bin bs=1M count=10 2>/dev/null" }, + { "command": "dd if=/dev/zero of=/srv/storage-lab/data/small.txt bs=1K count=2 2>/dev/null" } + ], + "validation": { + "commands": [ + { "description": "correctly identified cache/ as the largest consumer", "command": "cat /srv/storage-lab/biggest.txt", "expected_output": "cache", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "rm -rf /srv/storage-lab" } + ] +} diff --git a/scenarios/data/linux-troubleshooting-framework.json b/scenarios/data/linux-troubleshooting-framework.json new file mode 100644 index 0000000..e485ba6 --- /dev/null +++ b/scenarios/data/linux-troubleshooting-framework.json @@ -0,0 +1,37 @@ +{ + "id": "linux-troubleshooting-framework", + "title": "A Service Won't Start — Apply the Framework, Don't Just Guess", + "category": "Troubleshooting", + "isolation_type": "shared_namespace", + "difficulty": "Medium", + "type": "task", + "weight": 6, + "description": "## A Service Won't Start — Apply the Framework, Don't Just Guess\n\nA startup script at `/srv/trouble-lab/start.sh` is failing. Don't just poke at it randomly — apply OBSERVE → GATHER EVIDENCE → FORM HYPOTHESIS → TEST → REPAIR → VALIDATE → PREVENT.\n\n**Your task:**\n\n1. **Observe**: run `/srv/trouble-lab/start.sh` and note that it fails\n2. **Gather evidence**: the script's own error output tells you exactly what's missing — read it\n3. **Form a hypothesis and repair**: create whatever the error says is missing, with the correct content\n4. **Validate**: rerun the script and confirm it now succeeds AND that `/srv/trouble-lab/service.ready` is created", + "hints": [ + { + "title": "Reading the real error", + "body": "Redirect both stdout and stderr to a log file, then read it — the script's failure message names the exact missing path.", + "command": "/srv/trouble-lab/start.sh > /srv/trouble-lab/service.log 2>&1; cat /srv/trouble-lab/service.log" + }, + { + "title": "The fix", + "body": "The script expects a config file at a specific path with SOME content — any non-empty content satisfies it, since the script only checks that the file exists.", + "command": "mkdir -p /srv/trouble-lab/config\necho 'mode=production' > /srv/trouble-lab/config/settings.conf\n/srv/trouble-lab/start.sh" + } + ], + "setup_commands": [ + { "command": "mkdir -p /srv/trouble-lab" }, + { "command": "printf '#!/bin/bash\\nCONFIG=/srv/trouble-lab/config/settings.conf\\nif [ ! -f \"$CONFIG\" ]; then\\n echo \"FATAL: config file not found: $CONFIG\" >&2\\n exit 1\\nfi\\necho \"Started with config: $(cat $CONFIG)\"\\necho ready > /srv/trouble-lab/service.ready\\n' > /srv/trouble-lab/start.sh" }, + { "command": "chmod +x /srv/trouble-lab/start.sh" } + ], + "validation": { + "commands": [ + { "description": "the service now starts successfully", "command": "/srv/trouble-lab/start.sh >/dev/null 2>&1; echo $?", "expected_output": "0", "match": "exact" }, + { "description": "the ready marker was actually created", "command": "cat /srv/trouble-lab/service.ready", "expected_output": "ready", "match": "exact" } + ] + }, + "default_namespace": "default", + "teardown_commands": [ + { "command": "rm -rf /srv/trouble-lab" } + ] +} diff --git a/scenarios/data/linux-troubleshooting-order-mcq.json b/scenarios/data/linux-troubleshooting-order-mcq.json new file mode 100644 index 0000000..5ed5c80 --- /dev/null +++ b/scenarios/data/linux-troubleshooting-order-mcq.json @@ -0,0 +1,22 @@ +{ + "id": "linux-troubleshooting-order-mcq", + "title": "Where Does the Failure Actually Live?", + "category": "Troubleshooting", + "isolation_type": "shared_namespace", + "difficulty": "Medium", + "type": "mcq", + "weight": 4, + "description": "## Where Does the Failure Actually Live?\n\n`getent hosts myapp.internal` correctly returns an IP address. `curl http://myapp.internal:8080/` returns \"Connection refused.\" What does this combination actually tell you?", + "options": [ + { "id": "a", "text": "DNS is broken — fix the hosts file or DNS server first" }, + { "id": "b", "text": "The name resolves correctly, so the problem is downstream of DNS — most likely nothing is listening on port 8080 at that address" }, + { "id": "c", "text": "The application returned an error page" }, + { "id": "d", "text": "The network cable is unplugged" } + ], + "correct_option": "b", + "explanation": "A successful DNS resolution proves the name-to-IP step works — it tells you nothing about whether anything is listening on the port you're trying to reach. \"Connection refused\" specifically means a TCP SYN reached the destination and was actively rejected, most commonly because no process is listening on that port. This is exactly the kind of layer-by-layer reasoning the module's framework teaches: never re-diagnose a layer that already gave you a clean answer.", + "hints": [], + "setup_commands": [], + "default_namespace": "default", + "teardown_commands": [] +}