diff --git a/moonraker/components/machine.py b/moonraker/components/machine.py index 56eb1ff..352e86c 100644 --- a/moonraker/components/machine.py +++ b/moonraker/components/machine.py @@ -460,7 +460,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, retries=tries, + full_cmd, proc_input=proc_input, log_complete=False, attempts=tries, timeout=timeout ) @@ -959,7 +959,7 @@ class SystemdCliProvider(BaseProvider): ) prop_args = ",".join(properties) props: str = await self.shell_cmd.exec_cmd( - f"systemctl show -p {prop_args} {unit_name}", retries=5, + f"systemctl show -p {prop_args} {unit_name}", attempts=5, timeout=10. ) raw_props: Dict[str, Any] = {} @@ -1301,7 +1301,7 @@ class SupervisordCliProvider(BaseProvider): else: cmd = f"supervisorctl {args}" return await self.shell_cmd.exec_cmd( - cmd, proc_input=None, log_complete=False, retries=tries, + cmd, proc_input=None, log_complete=False, attempts=tries, timeout=timeout, success_codes=success_codes ) diff --git a/moonraker/components/shell_command.py b/moonraker/components/shell_command.py index d37ca63..959ae74 100644 --- a/moonraker/components/shell_command.py +++ b/moonraker/components/shell_command.py @@ -228,7 +228,7 @@ class ShellCommand: async def run_with_response( self, timeout: float = 2., - retries: int = 1, + attempts: int = 1, log_complete: bool = True, sig_idx: int = 1, proc_input: Optional[str] = None, @@ -236,11 +236,11 @@ class ShellCommand: ) -> str: async with self.run_lock: self.factory.add_running_command(self) - retries = max(1, retries) + attempts = max(1, attempts) stdin: Optional[bytes] = None if proc_input is not None: stdin = proc_input.encode() - while retries > 0: + while attempts > 0: self._reset_command_data() timed_out = False stdout = stderr = b"" @@ -271,7 +271,7 @@ class ShellCommand: f"\n{stdout.decode(errors='ignore')}") if self.cancelled and not timed_out: break - retries -= 1 + attempts -= 1 await asyncio.sleep(.5) self.factory.remove_running_command(self) raise ShellCommandError( @@ -375,7 +375,7 @@ class ShellCommandFactory: callback: OutputCallback = None, std_err_callback: OutputCallback = None, timeout: float = 2., - retries: int = 1, + attempts: int = 1, verbose: bool = True, sig_idx: int = 1, proc_input: Optional[str] = None, @@ -392,9 +392,9 @@ class ShellCommandFactory: scmd = ShellCommand( self, cmd, callback, std_err_callback, env, log_stderr, cwd ) - retries = max(1, retries) + attempts = max(1, attempts) async def _wrapper() -> None: - for _ in range(retries): + for _ in range(attempts): if await scmd.run( timeout, verbose, log_complete, sig_idx, proc_input, success_codes @@ -409,7 +409,7 @@ class ShellCommandFactory: self, cmd: str, timeout: float = 2., - retries: int = 1, + attempts: int = 1, sig_idx: int = 1, proc_input: Optional[str] = None, log_complete: bool = True, @@ -424,7 +424,7 @@ class ShellCommandFactory: scmd = ShellCommand(self, cmd, None, None, env, log_stderr, cwd) coro = scmd.run_with_response( - timeout, retries, log_complete, sig_idx, + timeout, attempts, log_complete, sig_idx, proc_input, success_codes ) return asyncio.create_task(coro) diff --git a/moonraker/components/update_manager/app_deploy.py b/moonraker/components/update_manager/app_deploy.py index 2706697..8308a71 100644 --- a/moonraker/components/update_manager/app_deploy.py +++ b/moonraker/components/update_manager/app_deploy.py @@ -424,7 +424,7 @@ class AppDeploy(BaseDeploy): try: await self.cmd_helper.run_cmd( f"{self.pip_cmd} install {args}", timeout=1200., notify=True, - retries=3, env=env, log_stderr=True + attempts=3, env=env, log_stderr=True ) except Exception: self.log_exc("Error updating python requirements") @@ -442,7 +442,7 @@ class AppDeploy(BaseDeploy): try: await self.cmd_helper.run_cmd( f"{self.pip_cmd} install pip=={update_ver}", - timeout=1200., notify=True, retries=3 + timeout=1200., notify=True, attempts=3 ) except Exception: self.log_exc("Error updating python pip") @@ -452,8 +452,9 @@ class AppDeploy(BaseDeploy): return None self.notify_status("Checking pip version...") try: - data: str = await self.cmd_helper.run_cmd_with_response( - f"{self.pip_cmd} --version", timeout=30., retries=3 + scmd = self.cmd_helper.get_shell_command() + data: str = await scmd.exec_cmd( + f"{self.pip_cmd} --version", timeout=30., attempts=3 ) match = re.match( r"^pip ([0-9.]+) from .+? \(python ([0-9.]+)\)$", data.strip() diff --git a/moonraker/components/update_manager/git_deploy.py b/moonraker/components/update_manager/git_deploy.py index 6d891c6..6f5eac8 100644 --- a/moonraker/components/update_manager/git_deploy.py +++ b/moonraker/components/update_manager/git_deploy.py @@ -217,7 +217,8 @@ class GitDeploy(AppDeploy): try: await self.cmd_helper.run_cmd( "npm ci --only=prod", notify=True, timeout=600., - cwd=str(self.path)) + cwd=str(self.path) + ) except Exception: self.notify_status("Node Package Update failed") @@ -442,17 +443,17 @@ class GitRepo: " is not a valid git repo") return False await self._wait_for_lock_release() - retries = 3 - while retries: + attempts = 3 + while attempts: self.git_messages.clear() try: cmd = "status --porcelain -b" - resp: Optional[str] = await self._run_git_cmd(cmd, retries=1) + resp: Optional[str] = await self._run_git_cmd(cmd, attempts=1) except Exception: - retries -= 1 + attempts -= 1 resp = None # Attempt to recover from "loose object" error - if retries and self.repo_corrupt: + if attempts and self.repo_corrupt: if not await self._repair_loose_objects(): # Since we are unable to recover, immediately # return @@ -669,8 +670,8 @@ class GitRepo: async with self.git_operation_lock: for _ in range(3): try: - await self._run_git_cmd(cmd, retries=1, corrupt_msg="error: ") - except self.cmd_helper.scmd_error as err: + await self._run_git_cmd(cmd, attempts=1, corrupt_msg="error: ") + except self.cmd_helper.get_shell_command().error as err: if err.return_code == 1: return False if self.repo_corrupt: @@ -788,7 +789,7 @@ class GitRepo: if self.git_remote == "?" or self.git_branch == "?": raise self.server.error("Cannot reset, unknown remote/branch") ref = f"{self.git_remote}/{self.git_branch}" - await self._run_git_cmd(f"reset --hard {ref}", retries=2) + await self._run_git_cmd(f"reset --hard {ref}", attempts=2) self.repo_corrupt = False async def fetch(self) -> None: @@ -800,7 +801,7 @@ class GitRepo: async def clean(self) -> None: self._verify_repo() async with self.git_operation_lock: - await self._run_git_cmd("clean -d -f", retries=2) + await self._run_git_cmd("clean -d -f", attempts=2) async def pull(self) -> None: self._verify_repo() @@ -859,7 +860,7 @@ class GitRepo: args = f"{cmd} {key} '{pattern}'" if pattern else f"{cmd} {key}" try: return await self.config_cmd(args) - except self.cmd_helper.scmd_error as e: + except self.cmd_helper.get_shell_command().error as e: if e.return_code == 1: return None raise @@ -884,9 +885,9 @@ class GitRepo: for attempt in range(3): try: return await self._run_git_cmd( - f"config {args}", retries=1, log_complete=verbose + f"config {args}", attempts=1, log_complete=verbose ) - except self.cmd_helper.scmd_error as e: + except self.cmd_helper.get_shell_command().error as e: if 1 <= (e.return_code or 10) <= 6 or attempt == 2: raise raise self.server.error("Failed to run git-config") @@ -907,7 +908,7 @@ class GitRepo: async def run_fsck(self) -> None: async with self.git_operation_lock: - await self._run_git_cmd("fsck --full", timeout=300., retries=1) + await self._run_git_cmd("fsck --full", timeout=300., attempts=1) async def clone(self) -> None: if self.is_submodule_or_worktree(): @@ -1194,12 +1195,13 @@ class GitRepo: "Attempting to repair loose objects..." ) try: - await self.cmd_helper.run_cmd_with_response( + shell_cmd = self.cmd_helper.get_shell_command() + await shell_cmd.exec_cmd( "find .git/objects/ -type f -empty | xargs rm", - timeout=10., retries=1, cwd=str(self.src_path)) + timeout=10., attempts=1, cwd=str(self.src_path)) await self._run_git_cmd_async( - "fetch --all -p", retries=1, fix_loose=False) - await self._run_git_cmd("fsck --full", timeout=300., retries=1) + "fetch --all -p", attempts=1, fix_loose=False) + await self._run_git_cmd("fsck --full", timeout=300., attempts=1) except Exception: msg = ( "Attempt to repair loose objects failed, " @@ -1216,7 +1218,7 @@ class GitRepo: async def _run_git_cmd_async(self, cmd: str, - retries: int = 5, + attempts: int = 5, need_git_path: bool = True, fix_loose: bool = True ) -> None: @@ -1231,10 +1233,11 @@ class GitRepo: git_cmd = f"git -C {self.src_path} {cmd}" else: git_cmd = f"git {cmd}" - scmd = self.cmd_helper.build_shell_command( + shell_cmd = self.cmd_helper.get_shell_command() + scmd = shell_cmd.build_shell_command( git_cmd, callback=self._handle_process_output, env=env) - while retries: + while attempts: self.git_messages.clear() self.fetch_input_recd = False self.fetch_timeout_handle = event_loop.delay_callback( @@ -1254,14 +1257,14 @@ class GitRepo: # Only attempt to repair loose objects once. Re-run # the command once. fix_loose = False - retries = 2 + attempts = 2 else: - # since the attept to repair failed, bypass retries + # since the attept to repair failed, bypass attempts # and immediately raise an exception raise self.server.error( "Unable to repair loose objects, use hard recovery" ) - retries -= 1 + attempts -= 1 await asyncio.sleep(.5) await self._check_lock_file_exists(remove=True) raise self.server.error(f"Git Command '{cmd}' failed") @@ -1303,21 +1306,22 @@ class GitRepo: self, git_args: str, timeout: float = 20., - retries: int = 5, + attempts: int = 5, env: Optional[Dict[str, str]] = None, corrupt_msg: str = "fatal: ", log_complete: bool = True ) -> str: + shell_cmd = self.cmd_helper.get_shell_command() try: - return await self.cmd_helper.run_cmd_with_response( + return await shell_cmd.exec_cmd( f"git -C {self.src_path} {git_args}", timeout=timeout, - retries=retries, + attempts=attempts, env=env, sig_idx=2, log_complete=log_complete ) - except self.cmd_helper.scmd_error as e: + except shell_cmd.error as e: stdout = e.stdout.decode().strip() stderr = e.stderr.decode().strip() msg_lines: List[str] = [] diff --git a/moonraker/components/update_manager/system_deploy.py b/moonraker/components/update_manager/system_deploy.py index 2a9e976..1bf907b 100644 --- a/moonraker/components/update_manager/system_deploy.py +++ b/moonraker/components/update_manager/system_deploy.py @@ -183,8 +183,8 @@ class AptCliProvider(BasePackageProvider): f"{self.APT_CMD} update", timeout=600., notify=notify) async def get_packages(self) -> List[str]: - res = await self.cmd_helper.run_cmd_with_response( - "apt list --upgradable", timeout=60.) + shell_cmd = self.cmd_helper.get_shell_command() + res = await shell_cmd.exec_cmd("apt list --upgradable", timeout=60.) pkg_list = [p.strip() for p in res.split("\n") if p.strip()] if pkg_list: pkg_list = pkg_list[2:] @@ -195,7 +195,8 @@ class AptCliProvider(BasePackageProvider): self.cmd_helper.notify_update_response("Resolving packages...") search_regex = "|".join([f"^{pkg}$" for pkg in package_list]) cmd = f"apt-cache search --names-only \"{search_regex}\"" - ret = await self.cmd_helper.run_cmd_with_response(cmd, timeout=600.) + shell_cmd = self.cmd_helper.get_shell_command() + ret = await shell_cmd.exec_cmd(cmd, timeout=600.) resolved = [ pkg.strip().split()[0] for pkg in ret.split("\n") if pkg.strip() ] @@ -217,7 +218,7 @@ class AptCliProvider(BasePackageProvider): pkgs = " ".join(resolved) await self.cmd_helper.run_cmd( f"{self.APT_CMD} install --yes {pkgs}", timeout=timeout, - retries=retries, notify=notify) + attempts=retries, notify=notify) async def upgrade_system(self) -> None: await self.cmd_helper.run_cmd( diff --git a/moonraker/components/update_manager/update_manager.py b/moonraker/components/update_manager/update_manager.py index dfed1ab..559b5d8 100644 --- a/moonraker/components/update_manager/update_manager.py +++ b/moonraker/components/update_manager/update_manager.py @@ -496,10 +496,6 @@ class CommandHelper: config.getboolean('enable_repo_debug', False, deprecate=True) if self.server.is_debug_enabled(): logging.warning("UPDATE MANAGER: REPO DEBUG ENABLED") - shell_cmd: SCMDComp = self.server.lookup_component('shell_command') - self.scmd_error = shell_cmd.error - self.build_shell_command = shell_cmd.build_shell_command - self.run_cmd_with_response = shell_cmd.exec_cmd self.pkg_updater: Optional[PackageDeploy] = None # database management @@ -527,6 +523,9 @@ class CommandHelper: def get_server(self) -> Server: return self.server + def get_shell_command(self) -> SCMDComp: + return self.server.lookup_component("shell_command") + def get_http_client(self) -> HttpClient: return self.http_client @@ -579,7 +578,7 @@ class CommandHelper: cmd: str, timeout: float = 20., notify: bool = False, - retries: int = 1, + attempts: int = 1, env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, sig_idx: int = 1, @@ -587,14 +586,10 @@ class CommandHelper: ) -> None: cb = self.notify_update_response if notify else None log_stderr |= self.server.is_verbose_enabled() - scmd = self.build_shell_command( - cmd, callback=cb, env=env, cwd=cwd, log_stderr=log_stderr + await self.get_shell_command().run_cmd_async( + cmd, cb, timeout=timeout, attempts=attempts, + env=env, cwd=cwd, sig_idx=sig_idx, log_stderr=log_stderr ) - for _ in range(retries): - if await scmd.run(timeout=timeout, sig_idx=sig_idx): - break - else: - raise self.server.error("Shell Command Error") def notify_update_refreshed(self) -> None: vinfo: Dict[str, Any] = {}