Beispiel #1
0
def test_details_from_command_error():
    details = errors.details_from_command_error(
        returncode=-1,
        cmd=["test-command", "flags", "quote$me"],
    )

    assert details == textwrap.dedent(
        """\
            * Command that failed: "test-command flags 'quote$me'"
            * Command exit code: -1"""
    )
Beispiel #2
0
def test_details_from_command_error_with_output_bytes():
    details = errors.details_from_command_error(
        returncode=-1,
        cmd=["test-command", "flags", "quote$me"],
        stdout=bytes.fromhex("00 FF"),
        stderr=bytes.fromhex("01 FE"),
    )

    assert details == textwrap.dedent(
        """\
            * Command that failed: "test-command flags 'quote$me'"
            * Command exit code: -1
            * Command output: b'\\x00\\xff'
            * Command standard error output: b'\\x01\\xfe'"""
    )
Beispiel #3
0
def test_details_from_command_error_with_output_strings():
    details = errors.details_from_command_error(
        returncode=-1,
        cmd=["test-command", "flags", "quote$me"],
        stdout="test stdout",
        stderr="test stderr",
    )

    assert details == textwrap.dedent(
        """\
            * Command that failed: "test-command flags 'quote$me'"
            * Command exit code: -1
            * Command output: 'test stdout'
            * Command standard error output: 'test stderr'"""
    )
    def transfer_destination_io(
        self, *, source: str, destination: io.BufferedIOBase, chunk_size: int = 4096
    ) -> None:
        """Transfer from source file to destination IO.

        Note that this can't use std{in,out}=open(...) due to LP #1849753.

        :param source: The source path, prefixed with <name:> for a path inside
            the instance.
        :param destination: An IO stream to write to.
        :param chunk_size: Number of bytes to transfer at a time.  Defaults to
            4096.

        :raises MultipassError: On error.
        """
        command = [str(self.multipass_path), "transfer", source, "-"]
        proc = subprocess.Popen(
            command,
            stdin=subprocess.DEVNULL,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )

        # Should never happen, but mypy/pyright makes noise.
        assert proc.stdout is not None
        assert proc.stderr is not None

        while True:
            data = proc.stdout.read(chunk_size)
            if not data:
                break

            destination.write(data)

        try:
            _, stderr = proc.communicate(timeout=30)
        except subprocess.TimeoutExpired:
            proc.kill()
            _, stderr = proc.communicate()

        if proc.returncode != 0:
            raise MultipassError(
                brief=f"Failed to transfer file {source!r}.",
                details=errors.details_from_command_error(
                    cmd=command, stderr=stderr, returncode=proc.returncode
                ),
            )
Beispiel #5
0
    def transfer_source_io(self,
                           *,
                           source: io.BufferedIOBase,
                           destination: str,
                           chunk_size: int = 4096) -> None:
        """Transfer to destination path with source IO.

        Note that this can't use std{in,out}=open(...) due to LP #1849753.

        :param source: An IO stream to read from.
        :param destination: The destination path, prefixed with <name:> for a
            path inside the instance.

        :raises MultipassError: On error.
        """
        command = [str(self.multipass_path), "transfer", "-", destination]
        proc = subprocess.Popen(command, stdin=subprocess.PIPE)

        # Should never happen, but pyright makes noise.
        assert proc.stdin is not None

        while True:
            data = source.read(chunk_size)
            if not data:
                break

            proc.stdin.write(data)

        # Wait until process is complete.
        try:
            _, stderr = proc.communicate(timeout=30)
        except subprocess.TimeoutExpired:
            proc.kill()
            _, stderr = proc.communicate()

        if proc.returncode != 0:
            raise MultipassError(
                brief=
                f"Failed to transfer file to destination {destination!r}.",
                details=errors.details_from_command_error(
                    cmd=command, stderr=stderr, returncode=proc.returncode),
            )