Improve error message if systemd is not running

Summary:
If the systemd user manager is not running (e.g. it crashed or was manually stopped), 'eden stop' fails with an unhelpful error message:

> pystemd.dbusexc.DBusConnectionRefusedError: [err -111]: Could not open a bus to DBus

or

> pystemd.dbusexc.DBusBaseError: [err -2]: Could not open a bus to DBus

Provide a better experience: tell the user the most likely cause, and suggest a solution:

> error: The systemd user manager is not running. Run the following command to start it, then try again:
> sudo systemctl start user@strager.service

Reviewed By: wez

Differential Revision: D13791023

fbshipit-source-id: 5172df0a52d21c311b27b8a527cad934f9882154
This commit is contained in:
Matt Glazar 2019-06-12 13:10:21 -07:00 committed by Facebook Github Bot
parent 6aa6599178
commit 5d8e8a5fcd
3 changed files with 56 additions and 3 deletions

View File

@ -9,6 +9,7 @@
import asyncio
import errno
import getpass
import os
import pathlib
import signal
@ -20,6 +21,8 @@ from .config import EdenInstance
from .logfile import forward_log_file
from .systemd import (
EdenFSSystemdServiceConfig,
SystemdConnectionRefusedError,
SystemdFileNotFoundError,
SystemdServiceFailedToStartError,
SystemdUserBus,
edenfs_systemd_service_name,
@ -274,6 +277,15 @@ def start_systemd_service(
start_task = loop.create_task(start_service_async())
loop.create_task(log_forwarder.poll_forever_async())
return loop.run_until_complete(start_task)
except (SystemdConnectionRefusedError, SystemdFileNotFoundError):
print_stderr(
f"error: The systemd user manager is not running. Run the "
f"following command to\n"
f"start it, then try again:\n"
f"\n"
f" sudo systemctl start user@{getpass.getuser()}.service"
)
return 1
except SystemdServiceFailedToStartError as e:
print_stderr(f"error: {e}")
return 1

View File

@ -21,6 +21,7 @@ import typing
pystemd_import_error = None
try:
import pystemd
import pystemd.dbusexc # pyre-ignore[21]: T32805591
import pystemd.dbuslib # pyre-ignore[21]: T32805591
import pystemd.systemd1.manager
import pystemd.systemd1.unit
@ -749,6 +750,18 @@ def escape_dbus_address(input: bytes) -> bytes:
return b"".join(result_pieces)
if pystemd_import_error is None:
SystemdConnectionRefusedError = (
pystemd.dbusexc.DBusConnectionRefusedError # pyre-ignore[16]: T32805591
)
SystemdFileNotFoundError = (
pystemd.dbusexc.DBusFileNotFoundError # pyre-ignore[16]: T32805591
)
else:
SystemdConnectionRefusedError = Exception
SystemdFileNotFoundError = Exception
class SystemdServiceFailedToStartError(Exception):
def __init__(
self,

View File

@ -8,6 +8,7 @@
# of patent rights can be found in the PATENTS file in the same directory.
import pathlib
import shutil
import subprocess
import sys
import typing
@ -147,15 +148,38 @@ class SystemdTest(
"journalctl doesn't work and should not be mentioned",
)
def test_eden_start_reports_error_if_systemd_is_not_running(self) -> None:
def test_eden_start_reports_error_if_systemd_is_dead(self) -> None:
self.set_up_edenfs_systemd_service()
systemd = self.systemd
assert systemd is not None
systemd.exit()
self.assertTrue(
(systemd.xdg_runtime_dir / "systemd" / "private").exists(),
"systemd's socket file should still exist",
)
self.spoof_user_name("testuser")
start_process = self.spawn_start_with_fake_edenfs()
# TODO(strager): Improve this message.
start_process.expect_exact("Could not open a bus to DBus")
start_process.expect_exact(
"error: The systemd user manager is not running. Run the following "
"command to\r\nstart it, then try again:"
)
start_process.expect_exact("sudo systemctl start user@testuser.service")
def test_eden_start_reports_error_if_systemd_is_dead_and_cleaned_up(self) -> None:
self.set_up_edenfs_systemd_service()
systemd = self.systemd
assert systemd is not None
systemd.exit()
shutil.rmtree(systemd.xdg_runtime_dir)
self.spoof_user_name("testuser")
start_process = self.spawn_start_with_fake_edenfs()
start_process.expect_exact(
"error: The systemd user manager is not running. Run the following "
"command to\r\nstart it, then try again:"
)
start_process.expect_exact("sudo systemctl start user@testuser.service")
def test_eden_start_uses_fallback_if_systemd_environment_is_missing(self) -> None:
self.set_up_edenfs_systemd_service()
@ -208,3 +232,7 @@ class SystemdTest(
config_d.mkdir()
with open(config_d / "systemd.toml", "w") as config_file:
toml.dump(config, config_file)
def spoof_user_name(self, user_name: str) -> None:
self.set_environment_variable("LOGNAME", user_name)
self.set_environment_variable("USER", user_name)