Beispiel #1
0
    def cloud_set_robot_contents(self, directory: str, workspace_id: str,
                                 robot_id: str) -> ActionResult:

        if not os.path.exists(directory):
            return ActionResult(
                False,
                f"Expected: {directory} to exist to upload to the cloud.")
        if not os.path.isdir(directory):
            return ActionResult(
                False,
                f"Expected: {directory} to be a directory to upload to the cloud.",
            )

        args = ["cloud", "push"]
        args.extend(["--directory", directory])
        args.extend(["--workspace", workspace_id])
        args.extend(["--robot", robot_id])

        args = self._add_config_to_args(args)
        error_action_result = self._add_account_to_args(args)
        if error_action_result is not None:
            return error_action_result

        ret = self._run_rcc(args, mutex_name=RCC_CLOUD_ROBOT_MUTEX_NAME)
        return ret
    def mock_run_rcc(self, args, *sargs, **kwargs):
        if args[:3] == ["cloud", "new", "--workspace"]:
            return ActionResult(
                success=True,
                message=None,
                result="Created new robot named 'New package 1597082853.2224553' with identity 453.\n",
            )
        if args[:3] == ["cloud", "push", "--directory"]:
            return ActionResult(success=True, message=None, result="OK.\n")

        raise AssertionError(f"Unexpected args: {args}")
Beispiel #3
0
    def cloud_create_robot(self, workspace_id: str,
                           robot_name: str) -> ActionResult[str]:
        from robocorp_ls_core.subprocess_wrapper import subprocess

        args = ["cloud", "new"]
        args.extend(["--workspace", workspace_id])
        args.extend(["--robot", robot_name])

        args = self._add_config_to_args(args)
        error_action_result = self._add_account_to_args(args)
        if error_action_result is not None:
            return error_action_result

        ret = self._run_rcc(args,
                            mutex_name=RCC_CLOUD_ROBOT_MUTEX_NAME,
                            stderr=subprocess.STDOUT)
        if not ret.success:
            return ret

        try:
            # Note: result is the package id.
            stdout = ret.result
            if not stdout:
                return ActionResult(
                    False,
                    f"No process stdout when creating new cloud package.")
            stdout = stdout.strip()

            # stdout is something as:
            # Created new robot named 'New package' with identity 1414.
            if not stdout.lower().startswith("created new"):
                return ActionResult(
                    False,
                    f'Expected output to start with "Created new". Found: {stdout}',
                )

            if stdout.endswith("."):
                stdout = stdout[:-1]

            package_id = stdout.split(" ")[-1]
            if not package_id:
                return ActionResult(
                    False, f"Unable to extract package id from: {stdout}")
        except Exception as e:
            log.exception("Error creating new robot.")
            return ActionResult(
                False,
                f"Unable to extract robot id from: {stdout}. Error: {e}")

        return ActionResult(ret.success, None, package_id)
Beispiel #4
0
    def get_template_names(self) -> ActionResult[List[str]]:
        result = self._run_rcc("robot initialize -l".split())
        if not result.success:
            return ActionResult(success=False, message=result.message)

        output = result.result
        if output is None:
            return ActionResult(success=False, message="Output not available")
        templates = []
        for line in output.splitlines():
            if line.startswith("- "):
                template_name = line[2:].strip()
                templates.append(template_name)

        return ActionResult(success=True, message=None, result=sorted(templates))
Beispiel #5
0
    def cloud_list_workspace_robots(
        self, workspace_id: str
    ) -> ActionResult[List[IRccRobotMetadata]]:
        import json

        ret: List[IRccRobotMetadata] = []
        args = ["cloud", "workspace"]
        args.extend(("--workspace", workspace_id))

        args = self._add_config_to_args(args)
        error_action_result = self._add_account_to_args(args)
        if error_action_result is not None:
            return error_action_result

        result = self._run_rcc(
            args, expect_ok=False, mutex_name=RCC_CLOUD_ROBOT_MUTEX_NAME
        )
        if not result.success:
            return ActionResult(False, result.message)

        output = result.result
        if not output:
            return ActionResult(
                False, "Error listing cloud workspace robots (output not available)."
            )

        try:
            workspace_info = json.loads(output)
        except Exception as e:
            log.exception(f"Error parsing json: {output}")
            return ActionResult(
                False,
                f"Error loading json obtained while listing cloud workspaces activities.\n{e}",
            )

        if not isinstance(workspace_info, dict):
            log.critical(f"Expected dict as top-level from json: {output}")
            msg = f"Unexpected type of cloud workspace activity json (expected dict, found: {type(workspace_info)}"
            return ActionResult(False, msg)

        for activity_info in workspace_info.get("activities", []):
            ret.append(
                RccRobotMetadata(
                    robot_id=activity_info["id"], robot_name=activity_info["name"]
                )
            )
        return ActionResult(True, None, ret)
 def custom_handler(args, *sargs, **kwargs):
     if args[:3] == ["cloud", "workspace", "--workspace"]:
         # List packages for workspace 1
         return ActionResult(
             success=False,
             message="""{"error":{"code":"WORKSPACE_TREE_NOT_FOUND","subCode":"","message":"workspace tree not found"}""",
             result=None,
         )
Beispiel #7
0
    def mock_run_rcc(self, args, *sargs, **kwargs):
        from robocorp_code.rcc import ACCOUNT_NAME

        if args[:5] == [
                "config", "credentials", "--account", ACCOUNT_NAME, "--delete"
        ]:
            return ActionResult(success=True, message=None, result="OK.\n")

        raise AssertionError(f"Unexpected args: {args}")
Beispiel #8
0
    def get_template_names(self) -> ActionResult[List[str]]:
        result = self._run_rcc("robot initialize -l".split())
        if not result.success:
            return ActionResult(success=False, message=result.message)

        output = result.result
        if output is None:
            return ActionResult(success=False, message="Output not available")
        templates = set()
        for line in output.splitlines():
            if line.startswith("- "):
                template_name = line[2:].strip()
                templates.add(template_name)

        ret: List[str] = []
        for key, description in self._TEMPLATES.items():
            if key in templates:
                ret.append(description)

        return ActionResult(success=True, message=None, result=ret)
    def mock_run_rcc(self, args, *sargs, **kwargs):
        if args[:5] == [
                "config",
                "credentials",
                "--account",
                "--robocorp-code",
                "--delete",
        ]:
            return ActionResult(success=True, message=None, result="OK.\n")

        raise AssertionError(f"Unexpected args: {args}")
Beispiel #10
0
    def cloud_list_workspaces(self) -> ActionResult[List[IRccWorkspace]]:
        import json

        ret: List[IRccWorkspace] = []
        args = ["cloud", "workspace"]
        args = self._add_config_to_args(args)
        error_action_result = self._add_account_to_args(args)
        if error_action_result is not None:
            return error_action_result

        result = self._run_rcc(
            args, expect_ok=False, mutex_name=RCC_CLOUD_ROBOT_MUTEX_NAME
        )

        if not result.success:
            return ActionResult(False, result.message)

        output = result.result
        if not output:
            return ActionResult(
                False, "Error listing cloud workspaces (output not available)."
            )

        try:
            lst = json.loads(output)
        except Exception as e:
            log.exception(f"Error parsing json: {output}")
            return ActionResult(
                False,
                f"Error loading json obtained while listing cloud workspaces.\n{e}",
            )
        for workspace_info in lst:
            ret.append(
                RccWorkspace(
                    workspace_id=workspace_info["id"],
                    workspace_name=workspace_info["name"],
                )
            )
        return ActionResult(True, None, ret)
Beispiel #11
0
    def _add_account_to_args(self, args: List[str]) -> Optional[ActionResult]:
        """
        Adds the account to the args.
        
        Returns an error ActionResult if unable to get a valid account.
        """
        account_info = self._last_verified_account_info
        if account_info is None:
            account_info = self.get_valid_account_info()
            if account_info is None:
                return ActionResult(False, "Unable to get valid account for action.")

        args.append("--account")
        args.append(account_info.account)
        return None
Beispiel #12
0
    def mock_run_rcc_default(self, args, *sargs, **kwargs) -> ActionResult:
        import json
        import copy

        if args[:4] == ["cloud", "workspace", "--workspace", "workspace_id_1"]:
            # List packages for workspace 1
            return ActionResult(success=True,
                                message=None,
                                result=json.dumps(self._package_info_ws_1))

        if args[:4] == ["cloud", "workspace", "--workspace", "workspace_id_2"]:
            # List packages for workspace 2
            return ActionResult(success=True,
                                message=None,
                                result=json.dumps(_PACKAGE_INFO_WS_2))

        if args[:3] == ["cloud", "workspace", "--config"]:
            # List workspaces
            workspace_info = _WS_INFO
            return ActionResult(success=True,
                                message=None,
                                result=json.dumps(workspace_info))

        if args[:3] == ["cloud", "push", "--directory"]:
            if args[4:8] == [
                    "--workspace", "workspace_id_1", "--robot", "2323"
            ]:
                return ActionResult(success=True)
            if args[4:8] == [
                    "--workspace", "workspace_id_1", "--robot", "453"
            ]:
                return ActionResult(success=True)

        if args[:5] == [
                "cloud", "new", "--workspace", "workspace_id_1", "--robot"
        ]:
            # Submit a new package to ws 1
            cp = copy.deepcopy(self._package_info_ws_1)
            cp["activities"].append({"id": "2323", "name": args[5]})
            self._package_info_ws_1 = cp

            return ActionResult(
                success=True,
                message=None,
                result="Created new robot named {args[5]} with identity 2323.",
            )

        raise AssertionError(f"Unexpected args: {args}")
Beispiel #13
0
    def mock_run_rcc_default(self, args, *sargs, **kwargs) -> ActionResult:
        import json
        import copy

        if self.custom_handler is not None:
            ret = self.custom_handler(args, *sargs, **kwargs)
            if ret is not None:
                return ret

        if args[:4] == ["cloud", "workspace", "--workspace", "workspace_id_1"]:
            # List packages for workspace 1
            return ActionResult(
                success=True, message=None, result=json.dumps(self._package_info_ws_1)
            )

        if args[:4] == ["cloud", "workspace", "--workspace", "workspace_id_2"]:
            # List packages for workspace 2
            return ActionResult(
                success=True, message=None, result=json.dumps(_PACKAGE_INFO_WS_2)
            )

        if args[:3] == ["cloud", "workspace", "--config"]:
            # List workspaces
            workspace_info = _WS_INFO
            return ActionResult(
                success=True, message=None, result=json.dumps(workspace_info)
            )

        if args[:3] == ["cloud", "push", "--directory"]:
            if args[4:8] == ["--workspace", "workspace_id_1", "--robot", "2323"]:
                return ActionResult(success=True)
            if args[4:8] == ["--workspace", "workspace_id_1", "--robot", "453"]:
                return ActionResult(success=True)

        if args[:5] == ["cloud", "new", "--workspace", "workspace_id_1", "--robot"]:
            # Submit a new package to ws 1
            cp = copy.deepcopy(self._package_info_ws_1)
            cp["activities"].append({"id": "2323", "name": args[5]})
            self._package_info_ws_1 = cp

            return ActionResult(
                success=True,
                message=None,
                result="Created new robot named {args[5]} with identity 2323.",
            )

        if args[:4] == ["config", "credentials", "-j", "--verified"]:
            return ActionResult(
                success=True,
                message=None,
                result=json.dumps(
                    [
                        {
                            "account": "robocorp-code",
                            "identifier": "001",
                            "endpoint": "https://endpoint.foo.bar",
                            "secret": "123...",
                            "verified": 1605525807,
                        }
                    ]
                ),
            )

        raise AssertionError(f"Unexpected args: {args}")
Beispiel #14
0
 def check_conda_installed(self, timeout=None) -> ActionResult[str]:
     # With mamba this is not needed anymore.
     # Note: api kept just for backward compatibility.
     return ActionResult(success=True, message=None, result="OK.")
Beispiel #15
0
    def _run_rcc(
        self,
        args: List[str],
        timeout: float = 30,
        error_msg: str = "",
        mutex_name=None,
        cwd: Optional[str] = None,
        log_errors=True,
        stderr=Sentinel.SENTINEL,
    ) -> ActionResult[str]:
        """
        Returns an ActionResult where the result is the stdout of the executed command.
        
        :param log_errors:
            If false, errors won't be logged (i.e.: should be false when errors
            are expected).
        """
        from robocorp_ls_core.basic import build_subprocess_kwargs
        from subprocess import check_output
        from robocorp_ls_core.subprocess_wrapper import subprocess

        if stderr is Sentinel.SENTINEL:
            stderr = subprocess.PIPE

        rcc_location = self.get_rcc_location()

        env = os.environ.copy()
        env.pop("PYTHONPATH", "")
        env.pop("PYTHONHOME", "")
        env.pop("VIRTUAL_ENV", "")
        env["PYTHONIOENCODING"] = "utf-8"
        env["PYTHONUNBUFFERED"] = "1"

        robocorp_home = self._get_robocorp_home()
        if robocorp_home:
            env["ROBOCORP_HOME"] = robocorp_home

        kwargs: dict = build_subprocess_kwargs(cwd, env, stderr=stderr)
        args = [rcc_location] + args + ["--controller", "RobocorpCode"]
        cmdline = " ".join([str(x) for x in args])

        try:
            if mutex_name:
                from robocorp_ls_core.system_mutex import timed_acquire_mutex
            else:
                timed_acquire_mutex = NULL
            with timed_acquire_mutex(mutex_name, timeout=15):
                boutput: bytes = check_output(args, timeout=timeout, **kwargs)

        except CalledProcessError as e:
            stdout = as_str(e.stdout)
            stderr = as_str(e.stderr)
            msg = f"Error running: {cmdline}.\nROBOCORP_HOME: {robocorp_home}\n\nStdout: {stdout}\nStderr: {stderr}"
            if log_errors:
                log.exception(msg)
            if not error_msg:
                return ActionResult(success=False, message=msg)
            else:
                additional_info = [error_msg]
                if stdout or stderr:
                    if stdout and stderr:
                        additional_info.append("\nDetails: ")
                        additional_info.append("\nStdout")
                        additional_info.append(stdout)
                        additional_info.append("\nStderr")
                        additional_info.append(stderr)

                    elif stdout:
                        additional_info.append("\nDetails: ")
                        additional_info.append(stdout)

                    elif stderr:
                        additional_info.append("\nDetails: ")
                        additional_info.append(stderr)

                return ActionResult(success=False,
                                    message="".join(additional_info))

        except Exception:
            msg = f"Error running: {args}"
            log.exception(msg)
            return ActionResult(success=False, message=msg)

        output = boutput.decode("utf-8", "replace")

        log.debug("Output from: %s:\n%s", cmdline, output)
        return ActionResult(success=True, message=None, result=output)
Beispiel #16
0
    def _run_rcc(
        self,
        args: List[str],
        timeout: float = 30,
        expect_ok=True,
        error_msg: str = "",
        mutex_name=None,
        cwd: Optional[str] = None,
    ) -> ActionResult[str]:
        """
        Returns an ActionResult where the result is the stdout of the executed command.
        """
        from robocorp_ls_core.basic import build_subprocess_kwargs
        from subprocess import check_output
        from robocorp_ls_core.subprocess_wrapper import subprocess

        rcc_location = self.get_rcc_location()

        env = os.environ.copy()
        env.pop("PYTHONPATH", "")
        env.pop("PYTHONHOME", "")
        env.pop("VIRTUAL_ENV", "")
        env["PYTHONIOENCODING"] = "utf-8"
        env["PYTHONUNBUFFERED"] = "1"

        kwargs: dict = build_subprocess_kwargs(cwd, env, stderr=subprocess.PIPE)
        args = [rcc_location] + args
        cmdline = " ".join([str(x) for x in args])

        try:
            if mutex_name:
                from robocorp_ls_core.system_mutex import timed_acquire_mutex
            else:
                timed_acquire_mutex = NULL
            with timed_acquire_mutex(mutex_name, timeout=15):
                boutput: bytes = check_output(args, timeout=timeout, **kwargs)

        except CalledProcessError as e:
            stdout = as_str(e.stdout)
            stderr = as_str(e.stderr)
            msg = f"Error running: {cmdline}.\nStdout: {stdout}\nStderr: {stderr}"
            log.exception(msg)
            if not error_msg:
                return ActionResult(success=False, message=msg)
            else:
                additional_info = [error_msg]
                if stdout or stderr:
                    if stdout and stderr:
                        additional_info.append("\nDetails: ")
                        additional_info.append("\nStdout")
                        additional_info.append(stdout)
                        additional_info.append("\nStderr")
                        additional_info.append(stderr)

                    elif stdout:
                        additional_info.append("\nDetails: ")
                        additional_info.append(stdout)

                    elif stderr:
                        additional_info.append("\nDetails: ")
                        additional_info.append(stderr)

                return ActionResult(success=False, message="".join(additional_info))

        except Exception:
            msg = f"Error running: {args}"
            log.exception(msg)
            return ActionResult(success=False, message=msg)

        output = boutput.decode("utf-8", "replace")

        log.debug(f"Output from: {cmdline}:\n{output}")
        if expect_ok:
            if "OK." in output:
                return ActionResult(success=True, message=None, result=output)
        else:
            return ActionResult(success=True, message=None, result=output)

        return ActionResult(
            success=False, message="OK. not found in message", result=output
        )