machine: validation fixes
Protect the sudo request with a lock. Add retries to the sudo commands. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
f0e4678fab
commit
00c4846d11
|
@ -86,6 +86,7 @@ class Machine:
|
|||
dist_info['release_info'] = distro.distro_release_info()
|
||||
self.inside_container = False
|
||||
self.moonraker_service_info: Dict[str, Any] = {}
|
||||
self.sudo_req_lock = asyncio.Lock()
|
||||
self._sudo_password: Optional[str] = None
|
||||
sudo_template = config.gettemplate("sudo_password", None)
|
||||
if sudo_template is not None:
|
||||
|
@ -282,50 +283,53 @@ class Machine:
|
|||
async def _set_sudo_password(
|
||||
self, web_request: WebRequest
|
||||
) -> Dict[str, Any]:
|
||||
self._sudo_password = web_request.get_str("password")
|
||||
if not await self.check_sudo_access():
|
||||
self._sudo_password = None
|
||||
raise self.server.error("Invalid password, sudo access was denied")
|
||||
sudo_responses = ["Sudo password successfully set."]
|
||||
restart: bool = False
|
||||
failed: List[Tuple[SudoCallback, str]] = []
|
||||
failed_msgs: List[str] = []
|
||||
if self.sudo_requests:
|
||||
while self.sudo_requests:
|
||||
cb, msg = self.sudo_requests.pop(0)
|
||||
try:
|
||||
ret = cb()
|
||||
if isinstance(ret, Awaitable):
|
||||
ret = await ret
|
||||
msg, need_restart = ret
|
||||
sudo_responses.append(msg)
|
||||
restart |= need_restart
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as e:
|
||||
failed.append((cb, msg))
|
||||
failed_msgs.append(str(e))
|
||||
restart = False if len(failed) > 0 else restart
|
||||
self.sudo_requests = failed
|
||||
if not restart and len(sudo_responses) > 1:
|
||||
# at least one successful response and not restarting
|
||||
eventloop = self.server.get_event_loop()
|
||||
eventloop.delay_callback(
|
||||
.05, self.server.send_event,
|
||||
"machine:sudo_alert",
|
||||
{
|
||||
"sudo_requested": self.sudo_requested,
|
||||
"request_messages": self.sudo_request_messages
|
||||
}
|
||||
)
|
||||
if failed_msgs:
|
||||
err_msg = "\n".join(failed_msgs)
|
||||
raise self.server.error(err_msg, 500)
|
||||
if restart:
|
||||
self.restart_moonraker_service()
|
||||
sudo_responses.append(
|
||||
"Moonraker is currently in the process of restarting."
|
||||
async with self.sudo_req_lock:
|
||||
self._sudo_password = web_request.get_str("password")
|
||||
if not await self.check_sudo_access():
|
||||
self._sudo_password = None
|
||||
raise self.server.error(
|
||||
"Invalid password, sudo access was denied"
|
||||
)
|
||||
sudo_responses = ["Sudo password successfully set."]
|
||||
restart: bool = False
|
||||
failed: List[Tuple[SudoCallback, str]] = []
|
||||
failed_msgs: List[str] = []
|
||||
if self.sudo_requests:
|
||||
while self.sudo_requests:
|
||||
cb, msg = self.sudo_requests.pop(0)
|
||||
try:
|
||||
ret = cb()
|
||||
if isinstance(ret, Awaitable):
|
||||
ret = await ret
|
||||
msg, need_restart = ret
|
||||
sudo_responses.append(msg)
|
||||
restart |= need_restart
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as e:
|
||||
failed.append((cb, msg))
|
||||
failed_msgs.append(str(e))
|
||||
restart = False if len(failed) > 0 else restart
|
||||
self.sudo_requests = failed
|
||||
if not restart and len(sudo_responses) > 1:
|
||||
# at least one successful response and not restarting
|
||||
eventloop = self.server.get_event_loop()
|
||||
eventloop.delay_callback(
|
||||
.05, self.server.send_event,
|
||||
"machine:sudo_alert",
|
||||
{
|
||||
"sudo_requested": self.sudo_requested,
|
||||
"request_messages": self.sudo_request_messages
|
||||
}
|
||||
)
|
||||
if failed_msgs:
|
||||
err_msg = "\n".join(failed_msgs)
|
||||
raise self.server.error(err_msg, 500)
|
||||
if restart:
|
||||
self.restart_moonraker_service()
|
||||
sudo_responses.append(
|
||||
"Moonraker is currently in the process of restarting."
|
||||
)
|
||||
return {
|
||||
"sudo_responses": sudo_responses,
|
||||
"is_restarting": restart
|
||||
|
@ -391,7 +395,7 @@ class Machine:
|
|||
return False
|
||||
return True
|
||||
|
||||
async def exec_sudo_command(self, command: str) -> str:
|
||||
async def exec_sudo_command(self, command: str, tries: int = 1) -> str:
|
||||
proc_input = None
|
||||
full_cmd = f"sudo {command}"
|
||||
if self._sudo_password is not None:
|
||||
|
@ -399,7 +403,7 @@ class Machine:
|
|||
full_cmd = f"sudo -S {command}"
|
||||
shell_cmd: SCMDComp = self.server.lookup_component("shell_command")
|
||||
return await shell_cmd.exec_cmd(
|
||||
full_cmd, proc_input=proc_input, log_complete=False
|
||||
full_cmd, proc_input=proc_input, log_complete=False, retries=tries
|
||||
)
|
||||
|
||||
def _get_sdcard_info(self) -> Dict[str, Any]:
|
||||
|
@ -861,7 +865,7 @@ class SystemdCliProvider(BaseProvider):
|
|||
)
|
||||
prop_args = ",".join(properties)
|
||||
props: str = await self.shell_cmd.exec_cmd(
|
||||
f"systemctl show -p {prop_args} {unit_name}"
|
||||
f"systemctl show -p {prop_args} {unit_name}", retries=5
|
||||
)
|
||||
raw_props: Dict[str, Any] = {}
|
||||
lines = [p.strip() for p in props.split("\n") if p.strip]
|
||||
|
@ -1276,6 +1280,7 @@ class InstallValidator:
|
|||
"must be updated manually."
|
||||
)
|
||||
if unit != "moonraker":
|
||||
logging.info(f"Custom service file detected: {unit}")
|
||||
# Not using he default unit name
|
||||
if app_args["is_default_data_path"]:
|
||||
# No datapath set, create a new, unique data path
|
||||
|
@ -1290,11 +1295,13 @@ class InstallValidator:
|
|||
f"data path '{new_dp}' already exists. Service file "
|
||||
"must be updated manually."
|
||||
)
|
||||
|
||||
# If the current path is bare we can remove it
|
||||
if self._check_path_bare(self.data_path):
|
||||
shutil.rmtree(self.data_path)
|
||||
self.data_path = new_dp
|
||||
if not self.data_path.exists():
|
||||
logging.info(f"New data path created at {self.data_path}")
|
||||
self.data_path.mkdir()
|
||||
# A non-default datapath requires successful update of the
|
||||
# service
|
||||
|
@ -1362,8 +1369,13 @@ class InstallValidator:
|
|||
% (SERVICE_VERSION, user, src_path, env_file, sys.executable)
|
||||
)
|
||||
try:
|
||||
await machine.exec_sudo_command(f"cp -f {tmp_svc} {svc_dest}")
|
||||
await machine.exec_sudo_command("systemctl daemon-reload")
|
||||
# write new environment
|
||||
env_file.write_text(ENVIRONMENT % (src_path, cmd_args))
|
||||
await machine.exec_sudo_command(
|
||||
f"cp -f {tmp_svc} {svc_dest}", tries=5)
|
||||
await machine.exec_sudo_command(
|
||||
"systemctl daemon-reload", tries=5
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception:
|
||||
|
@ -1374,9 +1386,8 @@ class InstallValidator:
|
|||
) from None
|
||||
finally:
|
||||
tmp_svc.unlink()
|
||||
# write new environment
|
||||
env_file.write_text(ENVIRONMENT % (src_path, cmd_args))
|
||||
self.data_path_valid = True
|
||||
self.sc_enabled = False
|
||||
return True
|
||||
|
||||
def _check_path_bare(self, path: pathlib.Path) -> bool:
|
||||
|
|
Loading…
Reference in New Issue