Lab — Python YAML Config Validator¶
Lab Overview¶
Purpose: Load infrastructure YAML safely and return automation-friendly exit codes.
Scenario: A PR adds deploy.yaml. CI should fail fast if the file is missing keys or is not valid YAML, without applying anything.
Expected outcome: A CLI that validates one or more YAML files and exits 0 (ok) or 2 (usage / invalid).
This is a lab, not a tutorial
Apply File Handling — pathlib, JSON, YAML, CSV and Error Handling and Exceptions.
Business Scenario¶
Platform requires every service repo to ship deploy.yaml with at least name (non-empty string) and replicas (integer ≥ 1). Your job is a local validator teammates can run before push — and CI can run without kubectl.
Learning Objectives¶
- Install PyYAML in a venv and pin a version
- Use
yaml.safe_load(never unsafeload) - Validate required keys and types
- Map validation failures to exit code 2
- Print human errors on stderr
Prerequisites¶
Knowledge¶
- File Handling — pathlib, JSON, YAML, CSV
- Error Handling and Exceptions
- CLI Applications — argparse, Click, Typer
- Configuration Management and Secrets
Software¶
| Tool | Notes |
|---|---|
| Python 3.12+ | python3 --version |
| PyYAML | pin e.g. PyYAML>=6.0,<7 |
Estimated cost: £0.
Architecture¶
Environment¶
mkdir -p ~/rebash-lab-python/configs
cd ~/rebash-lab-python
python3 -m venv .venv
source .venv/bin/activate
pip install 'PyYAML>=6.0,<7'
Initial State¶
cat > configs/deploy-ok.yaml << 'EOF'
name: payments-api
replicas: 2
EOF
cat > configs/deploy-bad.yaml << 'EOF'
name: payments-api
replicas: zero
EOF
cat > configs/not-yaml.yaml << 'EOF'
::: this is not yaml
EOF
Task¶
Step 1 – Validator skeleton¶
Create validate_config.py:
- argparse: positional
path(file) sys.exit(0)on successsys.exit(2)on any validation failure
Step 2 – Load safely¶
from pathlib import Path
import yaml
text = Path(path).read_text(encoding="utf-8")
data = yaml.safe_load(text)
Treat None / non-dict roots as invalid.
Step 3 – Schema checks¶
Require:
name:str, stripped length ≥ 1replicas:int, ≥ 1
Collect all errors (do not stop at the first) and print each on stderr.
Step 4 – Multi-file mode (stretch)¶
Accept multiple paths; exit 2 if any file fails.
Validation¶
python validate_config.py configs/deploy-ok.yaml; echo $? # 0
python validate_config.py configs/deploy-bad.yaml; echo $? # 2
python validate_config.py configs/not-yaml.yaml; echo $? # 2
python validate_config.py missing.yaml; echo $? # 2
- Valid file exits
0 - Bad replicas / broken YAML / missing file exit
2 - Errors go to stderr; success may be silent or one line on stdout
- Code uses
safe_loadonly
Troubleshooting¶
| Symptom | Fix |
|---|---|
YAMLLoadWarning | You used unsafe load — switch to safe_load |
replicas: "2" passes | Coerce or reject strings; require isinstance(..., int) and not bool |
| Relative path fails | Resolve with Path(path).expanduser().resolve() |
Cleanup¶
deactivate 2>/dev/null || true
# keep shared ~/rebash-lab-python if used by other labs, or:
rm -rf ~/rebash-lab-python
Production Discussion¶
Prefer JSON Schema or a typed model (Pydantic) for larger contracts. Keep validators pure (no cluster calls) so CI stays fast and credential-free.
Stretch Goals¶
- Validate optional
imagematchesrepo:tag - Add
--strictto forbid unknown keys - Return a JSON report for CI annotations
Related¶
- Track: Python for DevOps
- Previous: Python Linux Health Checker
- Next: Python JSON Validator
- Cheat sheet: Python