Spaces:
Sleeping
Sleeping
| """The three task scenarios. Each builds a fresh VFS in a specific broken state.""" | |
| from __future__ import annotations | |
| import hashlib | |
| from typing import Dict, Tuple | |
| from disk_panic.server.vfs import VFS, VFSFile | |
| # Sentinel content for the protected audit directory. Hash is captured at reset. | |
| _AUDIT_FILES = { | |
| "/var/log/audit/audit.log": b"type=LOGIN audit_id=001 user=root\n" * 64, | |
| "/var/log/audit/audit.log.1": b"type=LOGIN audit_id=000 user=root\n" * 128, | |
| } | |
| def _base_fs() -> VFS: | |
| vfs = VFS() | |
| # /etc skeleton | |
| vfs.files["/etc/hostname"] = VFSFile(b"prod-app-01\n", 12) | |
| vfs.files["/etc/os-release"] = VFSFile(b"NAME=Linux\n", 11) | |
| # /var/log with audit (protected) and some normal logs | |
| for path, content in _AUDIT_FILES.items(): | |
| vfs.files[path] = VFSFile(content=content, size=len(content), protected=True) | |
| vfs.files["/var/log/syslog"] = VFSFile(b"boot ok\n", 256 * 1024) | |
| vfs.files["/var/log/nginx/access.log"] = VFSFile(b"", 128 * 1024 * 1024) # 128M | |
| # /app binary | |
| vfs.files["/opt/app/app.bin"] = VFSFile(b"#!/bin/app\n", 40 * 1024 * 1024) | |
| return vfs | |
| def build_easy() -> Tuple[VFS, Dict[str, object]]: | |
| vfs = _base_fs() | |
| # One huge rotated log is nearly filling the disk. | |
| vfs.files["/var/log/nginx/access.log.1.gz"] = VFSFile( | |
| content=b"\x1f\x8b" + b"X" * 1024, | |
| size=int(8.7 * 1024 ** 3), # 8.7G | |
| ) | |
| vfs.services["app"] = "active" | |
| targets = { | |
| "audit_sha": _audit_sha(vfs), | |
| "requires_service_active": False, | |
| "requires_logrotate": False, | |
| } | |
| return vfs, targets | |
| def build_medium() -> Tuple[VFS, Dict[str, object]]: | |
| vfs = _base_fs() | |
| vfs.files["/var/log/journal/system.journal"] = VFSFile( | |
| content=b"", size=int(8.5 * 1024 ** 3) | |
| ) | |
| # Service failed because disk was full | |
| vfs.services["app"] = "failed" | |
| targets = { | |
| "audit_sha": _audit_sha(vfs), | |
| "requires_service_active": True, | |
| "requires_logrotate": False, | |
| } | |
| return vfs, targets | |
| def build_hard() -> Tuple[VFS, Dict[str, object]]: | |
| vfs = _base_fs() | |
| # Runaway process writing 100 MiB per step to /var/log/app/runaway.log | |
| vfs.files["/var/log/app/runaway.log"] = VFSFile( | |
| content=b"", size=int(9.0 * 1024 ** 3) | |
| ) | |
| vfs.runaway_path = "/var/log/app/runaway.log" | |
| vfs.runaway_rate = 100 * 1024 * 1024 # 100 MiB/step | |
| vfs.services["app"] = "failed" | |
| targets = { | |
| "audit_sha": _audit_sha(vfs), | |
| "requires_service_active": True, | |
| "requires_logrotate": True, | |
| "logrotate_path": "/etc/logrotate.d/app", | |
| } | |
| return vfs, targets | |
| def _audit_sha(vfs: VFS) -> str: | |
| h = hashlib.sha256() | |
| for p in sorted(vfs.files): | |
| if p.startswith("/var/log/audit/"): | |
| h.update(p.encode()) | |
| h.update(b"\0") | |
| h.update(vfs.files[p].content) | |
| return h.hexdigest() | |
| SCENARIOS = { | |
| "easy": build_easy, | |
| "medium": build_medium, | |
| "hard": build_hard, | |
| } | |
| TASK_ORDER = ["easy", "medium", "hard"] | |