mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
|
|
"""
|
||
|
|
Entry point for wrolpi-mounts.service.
|
||
|
|
|
||
|
|
This module is intentionally tiny: it wires up production dependencies
|
||
|
|
(real subprocess executor, real wrolpi uid/gid lookup, real fstab.yaml on
|
||
|
|
the drive) and asks the Reconciler to apply. All real work lives in
|
||
|
|
controller.lib.reconciler; tests for the orchestration live in
|
||
|
|
test_reconciler.py using FakeMountExecutor.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import sys
|
||
|
|
|
||
|
|
from controller.lib.disks import get_wrolpi_uid_gid
|
||
|
|
from controller.lib.fstab_migration import migrate_etc_fstab
|
||
|
|
from controller.lib.mount_executor import SubprocessMountExecutor
|
||
|
|
from controller.lib.reconciler import Reconciler
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
def main(argv=None) -> int:
|
||
|
|
logging.basicConfig(
|
||
|
|
level=logging.INFO,
|
||
|
|
format="%(levelname)s wrolpi-mounts: %(message)s",
|
||
|
|
)
|
||
|
|
|
||
|
|
# Migration is idempotent (marker-gated). Doing it here means a freshly
|
||
|
|
# upgraded WROLPi picks up legacy /etc/fstab entries on the first boot
|
||
|
|
# of the new mount system, before we try to apply them.
|
||
|
|
try:
|
||
|
|
migrate_etc_fstab()
|
||
|
|
except Exception as e: # noqa: BLE001 - never fail boot on migration error
|
||
|
|
logger.error("fstab migration raised, continuing without it: %s", e)
|
||
|
|
|
||
|
|
try:
|
||
|
|
wrolpi_uid, wrolpi_gid = get_wrolpi_uid_gid()
|
||
|
|
except Exception as e: # noqa: BLE001 - best-effort enrichment
|
||
|
|
logger.warning("Could not resolve wrolpi uid/gid (%s)", e)
|
||
|
|
wrolpi_uid, wrolpi_gid = None, None
|
||
|
|
|
||
|
|
reconciler = Reconciler(
|
||
|
|
executor=SubprocessMountExecutor(),
|
||
|
|
wrolpi_uid=wrolpi_uid,
|
||
|
|
wrolpi_gid=wrolpi_gid,
|
||
|
|
)
|
||
|
|
result = reconciler.apply()
|
||
|
|
|
||
|
|
logger.info(
|
||
|
|
"Reconcile complete: %d mounted, %d unmounted, %d mount failures, %d unmount failures",
|
||
|
|
len(result.mounted), len(result.unmounted),
|
||
|
|
len(result.mount_failures), len(result.unmount_failures),
|
||
|
|
)
|
||
|
|
for mp, err in result.mount_failures:
|
||
|
|
logger.error("Failed to mount %s: %s", mp, err)
|
||
|
|
for mp, err in result.unmount_failures:
|
||
|
|
logger.error("Failed to unmount %s: %s", mp, err)
|
||
|
|
|
||
|
|
# Always return 0: the service must not block boot if a single mount
|
||
|
|
# is broken. Failures are logged and surfaced via the journal.
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
sys.exit(main())
|