Skip to content

Add support for running process asynchronously#835

Open
JoshuaWatt wants to merge 12 commits into
labgrid-project:masterfrom
JoshuaWatt:command-background
Open

Add support for running process asynchronously#835
JoshuaWatt wants to merge 12 commits into
labgrid-project:masterfrom
JoshuaWatt:command-background

Conversation

@JoshuaWatt

Copy link
Copy Markdown
Contributor

Description
Adds support for running processes asynchronously with the main test code. Attempts to incorporate the feed back from #552

Checklist

  • Documentation for the feature
  • Tests for the feature
  • The arguments and description in doc/configuration.rst have been updated
  • Add a section on how to use the feature to doc/usage.rst
  • Add a section on how to use the feature to doc/development.rst
  • CHANGES.rst has been updated
  • PR has been tested
  • Man pages have been regenerated

@codecov

codecov Bot commented Sep 16, 2021

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.54902% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.6%. Comparing base (d3c08b1) to head (ad293fa).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
labgrid/driver/bareboxdriver.py 28.5% 25 Missing ⚠️
labgrid/driver/shelldriver.py 37.9% 18 Missing ⚠️
labgrid/driver/ubootdriver.py 37.9% 18 Missing ⚠️
labgrid/util/marker.py 80.8% 18 Missing ⚠️
labgrid/driver/sshdriver.py 91.8% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master    #835     +/-   ##
========================================
+ Coverage    45.9%   46.6%   +0.6%     
========================================
  Files         180     181      +1     
  Lines       14496   14760    +264     
========================================
+ Hits         6666    6880    +214     
- Misses       7830    7880     +50     
Flag Coverage Δ
3.10 46.6% <72.5%> (+0.6%) ⬆️
3.11 46.6% <72.5%> (+0.6%) ⬆️
3.12 46.6% <72.5%> (+0.6%) ⬆️
3.13 46.5% <72.5%> (+0.6%) ⬆️
3.14 46.5% <72.5%> (+0.6%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@Emantor

Emantor commented Sep 17, 2021

Copy link
Copy Markdown
Member

Overall this design looks like what we had in mind in #552. Thank you very much for the implementation, looking forward to testing this on my lab at home.

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

It is unfortunately a little hard to write automated tests for most of this since it requires actual hardware interaction, but I'll take a stab at it. I was able to test on actual hardware (Raspberry Pi 4 w/ u-boot) with the following test suite:

import socket
import os
import time

def test_uboot_async(uboot_command):
    with uboot_command.popen("echo hello") as p:
        assert p.read(4).decode("utf-8").splitlines() == ["hell"]
        assert p.read().decode("utf-8").splitlines() == ["o"]

    with uboot_command.popen("echo bzap hello bzap") as p:
        index, before, match, after = p.expect("hello")
        assert index == 0
        assert match.group(0).decode("utf-8") == "hello"
        assert before.decode("utf-8") == "bzap "
        assert after.decode("utf-8") == "hello"
        assert p.read().decode("utf-8").splitlines() == [" bzap"]

def test_ssh_async(ssh_command):
    with ssh_command.popen("echo hello") as p:
        assert p.read(4).decode("utf-8").splitlines() == ["hell"]
        assert p.read().decode("utf-8").splitlines() == ["o"]

    with ssh_command.popen("echo bzap hello bzap") as p:
        index, before, match, after = p.expect("hello")
        assert index == 0
        assert match.group(0).decode("utf-8") == "hello"
        assert before.decode("utf-8") == "bzap "
        assert after.decode("utf-8") == "hello"
        assert p.read().decode("utf-8").splitlines() == [" bzap"]

def test_shell_nc(shell_command, ssh_command):
    with shell_command.popen("nc -l -p 5555") as p:
        # Wait for netcat to start listening
        time.sleep(2)
        with ssh_command.forward_local_port(5555) as local_port:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect(("localhost", local_port))
                s.sendall(b"Hello World")
            p.expect(b"Hello World")
            time.sleep(2)

def test_ssh_nc(ssh_command):
    with ssh_command.popen("nc -l -p 5555 127.0.0.1") as p:
        # Wait for netcat to start listening
        time.sleep(2)
        with ssh_command.forward_local_port(5555) as local_port:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect(("localhost", local_port))
                s.sendall(b"Hello World")
            p.expect(b"Hello World")
            time.sleep(2)


def test_shell_async(shell_command):
    with shell_command.popen("echo hello") as p:
        assert p.read(4).decode("utf-8").splitlines() == ["hell"]
        assert p.read().decode("utf-8").splitlines() == ["o"]

    with shell_command.popen("echo bzap hello bzap") as p:
        index, before, match, after = p.expect("hello")
        assert index == 0
        assert match.group(0).decode("utf-8") == "hello"
        assert before.decode("utf-8") == "bzap "
        assert after.decode("utf-8") == "hello"
        assert p.read().decode("utf-8").splitlines() == [" bzap"]

def test_shell_interrupt(shell_command):
    with shell_command.popen("yes") as p:
        l = p.read(1024).decode("utf-8").splitlines()
        assert l == ["y"] * len(l)

    shell_command.run_check("true")

Note that I wasn't able to test the barebox driver, so manual testing there would be appreciated

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

I added some tests, which revealed a few bugs that needed fixed. I think it's all good to go now though (although I still have no way to test barebox)

@Bastian-Krause Bastian-Krause left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I found some trivial things. I'll have to dive deeper into this later.

This should be mentioned in CHANGES.rst and probably needs some documentation elsewhere?

Comment thread labgrid/protocol/commandprotocol.py Outdated
Comment thread labgrid/driver/bareboxdriver.py Outdated
Comment thread labgrid/driver/shelldriver.py Outdated
Comment thread labgrid/driver/sshdriver.py Outdated
Comment thread labgrid/protocol/commandprotocol.py Outdated
Comment thread labgrid/driver/ubootdriver.py Outdated
Comment thread labgrid/util/expect.py
Comment thread labgrid/util/marker.py Outdated
Comment thread labgrid/util/marker.py Outdated
Comment thread labgrid/util/marker.py Outdated
@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

Great work! I found some trivial things. I'll have to dive deeper into this later.

This should be mentioned in CHANGES.rst and probably needs some documentation elsewhere?

I added a little to CHANGES.rst; I can add more documentation but I'm not really sure where it should go.

Comment thread labgrid/protocol/commandprotocol.py Outdated
@JoshuaWatt JoshuaWatt force-pushed the command-background branch 2 times, most recently from 580e931 to 88b4826 Compare September 22, 2021 14:07
@Emantor Emantor added this to the v0.5.0 milestone Sep 22, 2021

@Bastian-Krause Bastian-Krause left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this briefly now.

Issues I stumbled upon:

  • BareboxDriver now times out on run()
  • running multiple processes on BareboxDriver/ShellDriver is not forbidden, but runs in timeouts
  • calling stop() on ConsoleMarkerProcess returned by ShellDriver.popen() leads to a timeout (e.g. stopping a "sleep 100") - the implementation looks incomplete, I don't see the prompt being detected on stop()

Open questions:

  • why is SSHDriver.run() not using popen()?
  • I do not really understand the difference between read_nonblocking()/read()

Styling:

  • we use f-strings where appropriate since #838, please do the same

Note: this is no comprehensive review yet, just a collection of things I saw while testing with a QEMU and a physical target.

Comment thread labgrid/util/marker.py Outdated

@step(args=['size', 'timeout'], result=True)
def read(self, size=-1, *, timeout=None):
t = OptionalTimeout(timeout, self._console.timeout)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QEMUDriver and ExternalConsoleDriver do not implement a timeout attribute.

Comment thread labgrid/driver/bareboxdriver.py Outdated
output = p.read(timeout=timeout)
# Remove VT100 Codes and split by newline
data = self.re_vt100.sub('', output.decode('utf-8')).split('\r\n')[1:-1]
self.logger.debug(f"Received Data: {data}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log functions should not use f-strings, but rather lazy % formatting. There are multiple occurrences of this spread in this PR.

@JoshuaWatt JoshuaWatt Sep 28, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why? It seem confusing to developers to mix the two styles without a good reason :/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is a reason. Here is a short wrap-up: https://medium.com/swlh/why-it-matters-how-you-log-in-python-1a1085851205

TL;DR: formatting is only done if required (i.e. the log level is reached) and errors are handled gracefully.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the article has confused me more; IIUC, the article is suggesting that logging should use lazy evaluation by passing format arguments as parameters (not using %), in which case the log message would be something like self.logger.debug("Received data: %s", data). However, it seems you are suggesting I use % instead, which is not a lazy evalutation and therefore not superior (at least in terms of what the article is discussing) from f-strings.

I think what you want to do is either use actual lazy evaluation for log messages, or non-lazy f-strings for consistency with everywhere else f-strings are used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "% formatting" I mean logger.debug("%s", data).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I will do that

Comment thread CHANGES.rst Outdated
- AndroidFastbootDriver now supports booting/flashing images preconfigured in
the environment configuration.
- CommandProtocol now supports a popen() method to run a process on the target
asynchronously with the pytest code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why mention pytest here? The PR does not touch anything pytest-specific.

Comment thread labgrid/driver/ubootdriver.py Outdated
data = self.re_vt100.sub(
'', output.decode('utf-8'), count=1000000
).replace("\r", "").split("\n")
self.logger.debug("Received Data: {data}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is meant as an f-string. Please convert to lazy % formatting.

@JoshuaWatt JoshuaWatt Oct 1, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per previous discussion, % is not lazy

Comment thread labgrid/util/marker.py Outdated

def gen_marker():
return ''.join(random.choice(string.ascii_uppercase) for i in range(10))
return "".join(random.choice(string.ascii_uppercase) for i in range(10))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change this line.

Comment thread labgrid/util/timeout.py Outdated
Comment on lines +33 to +34
will never expire. If timeout negative and default is default is not None,
the default timer value will be used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence needs revisiting.

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

I've tested this briefly now.

Issues I stumbled upon:

* `BareboxDriver` now times out on `run()`

I don't have any way of testing barebox, can you provide more information so I can fix it?

* running multiple processes on `BareboxDriver`/`ShellDriver` is not forbidden, but runs in timeouts

Do we want to explicitly disallow that? I'm fine with that if that's what everyone wants to do.

* calling `stop()` on `ConsoleMarkerProcess` returned by `ShellDriver.popen()` leads to a timeout (e.g. stopping a "sleep 100") - the implementation looks incomplete, I don't see the prompt being detected on `stop()`

Open questions:

* why is `SSHDriver.run()` not using `popen()`?

It would be nice, but I can't do that without breaking the SSH driver because of the stderr_merge option, which is not possible to implement with popen()

* I do not really understand the difference between `read_nonblocking()`/`read()`

read_nonblocking allows a CommandProcessProcotol to be directly used as the backend for a popen.spawn object, since the latter requires the the read_nonblocking function to be implemented. read is the API for normal folks :)

Styling:

* we use f-strings where appropriate since #838, please do the same

I tried to use f-strings everywhere.... IHMO it's confusing to developers to require different string formatting for different uses (unless there is some really compelling performance reason to do otherwise).

Note: this is no comprehensive review yet, just a collection of things I saw while testing with a QEMU and a physical target.

@Bastian-Krause

Bastian-Krause commented Sep 28, 2021

Copy link
Copy Markdown
Member
  • BareboxDriver now times out on run()

I don't have any way of testing barebox, can you provide more information so I can fix it?

Here is my demo script:

#!/usr/bin/env python3

import labgrid

labgrid.StepReporter()

env = labgrid.Environment("beaglebone.yaml")
target = env.get_target()

barebox = target.get_driver("BareboxDriver")
target.activate(barebox)

barebox.run("echo hi")

This results in:
labgrid-barebox-run.log

I have not looked into it yet.

@JoshuaWatt JoshuaWatt force-pushed the command-background branch 2 times, most recently from a2719bd to 53b2f8c Compare October 4, 2021 15:59
@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

I think I've addressed most of the comment, is there anything else needed to merge this?

@Bastian-Krause

Copy link
Copy Markdown
Member

@JoshuaWatt Thanks, re-requesting review or a short notice like yours is always welcome, so we know when to look at PRs again. I need to look at/test the current state again. I'll come back to you.

@Bastian-Krause

Bastian-Krause commented Oct 28, 2021

Copy link
Copy Markdown
Member

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.

Another thing: how would you read the output of a long-running command (e.g. find /) asynchronously? Could you provide an example? I tried something with while p.poll() is None, but the poll() method blocks for a second if the process is still alive, so that feels wrong.

@Bastian-Krause

Copy link
Copy Markdown
Member

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.

Ah, this has nothing to do with the long-running command. The context manager hits CTRL-c on close, which prevents the second marker from being echoed (on the shell, not in barebox though). That's why we run into the timeout there.

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.

Another thing: how would you read the output of a long-running command (e.g. find /) asynchronously? Could you provide an example? I tried something with while p.poll() is None, but the poll() method blocks for a second if the process is still alive, so that feels wrong.

poll is for checking if the process is still alive; to read output use p.read() or p.expect()

@JoshuaWatt

JoshuaWatt commented Nov 2, 2021

Copy link
Copy Markdown
Contributor Author

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.

Ah, this has nothing to do with the long-running command. The context manager hits CTRL-c on close, which prevents the second marker from being echoed (on the shell, not in barebox though). That's why we run into the timeout there.

Does something need to change? It seems like it's doing what you are asking for (launching the process, then immediately terminating it). If you want to wait for it to exit normally, use p.poll() in a loop, or p.wait()

@Bastian-Krause

Copy link
Copy Markdown
Member

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.
Another thing: how would you read the output of a long-running command (e.g. find /) asynchronously? Could you provide an example? I tried something with while p.poll() is None, but the poll() method blocks for a second if the process is still alive, so that feels wrong.

poll is for checking if the process is still alive; to read output use p.read() or p.expect()

Okay, will try that.

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass
  
This ends up with a `TIMEOUT` exception.

Ah, this has nothing to do with the long-running command. The context manager hits CTRL-c on close, which prevents the second marker from being echoed (on the shell, not in barebox though). That's why we run into the timeout there.

Does something need to change? It seems like it's doing what you are asking for (launching the process, then immediately terminating it). If you want to wait for it to exit normally, use p.poll() in a loop, or p.wait()

I think the behavior between barebox and shell should be consistent. I would not expect to get a TIMEOUT exception if I do not wait for the process to finish. Do you agree?

@JoshuaWatt

JoshuaWatt commented Nov 3, 2021

Copy link
Copy Markdown
Contributor Author

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass

This ends up with a TIMEOUT exception.
Another thing: how would you read the output of a long-running command (e.g. find /) asynchronously? Could you provide an example? I tried something with while p.poll() is None, but the poll() method blocks for a second if the process is still alive, so that feels wrong.

poll is for checking if the process is still alive; to read output use p.read() or p.expect()

Okay, will try that.

How would you run a command that takes longer than 30 seconds?

def test_async_output(strategy):
    strategy.transition("shell")
    with strategy.shell.popen("sleep 40") as p:
        pass
  
This ends up with a `TIMEOUT` exception.

Ah, this has nothing to do with the long-running command. The context manager hits CTRL-c on close, which prevents the second marker from being echoed (on the shell, not in barebox though). That's why we run into the timeout there.

Does something need to change? It seems like it's doing what you are asking for (launching the process, then immediately terminating it). If you want to wait for it to exit normally, use p.poll() in a loop, or p.wait()

I think the behavior between barebox and shell should be consistent. I would not expect to get a TIMEOUT exception if I do not wait for the process to finish. Do you agree?

Ya, that makes sense; let me see what I can do

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

@Bastian-Krause Ok, I tracked this down to a behavior difference is bash (which I was using) vs. busybox. I've corrected it and now it should work the same for both shells

@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

For those interested, the difference is:

bash:

raspberrypi4-64:~# echo -n 'ABC'; sleep 10; echo 'ABC' $?; 
ABC^C
ABC 130
raspberrypi4-64:~#

busybox:

raspberrypi4-64:~# echo -n 'ABC'; sleep 10; echo 'ABC' $?;
ABC^C
raspberrypi4-64:~#

Note that interrupting the command on busybox prevents all subsequent commands (the echo with the exit code) from running, where as they still run under bash.

@Bastian-Krause Bastian-Krause removed this from the v23.1 milestone Jul 20, 2023
@JoshuaWatt JoshuaWatt force-pushed the command-background branch from 58d847d to ba12014 Compare June 3, 2025 19:11
@JoshuaWatt JoshuaWatt force-pushed the command-background branch 2 times, most recently from a9e0e17 to 8546a36 Compare June 12, 2025 20:56
@JoshuaWatt JoshuaWatt force-pushed the command-background branch 2 times, most recently from 489db0e to ac03245 Compare July 3, 2025 19:53
@JPEWdev

JPEWdev commented Aug 14, 2025

Copy link
Copy Markdown
Contributor

@Bastian-Krause I think this is ready, would you mind looking again?

@Bastian-Krause

Copy link
Copy Markdown
Member

Thanks for keeping at it! I'll try to have a look at this and run our network test suite against this.

@Bastian-Krause

Copy link
Copy Markdown
Member

Thanks for keeping at it! I'll try to have a look at this and run our network test suite against this.

Due to our tasks shifting, I did not work on our (currently internal) test suite, thus did not manage to get this tested. Sorry. If anyone else could give this a spin, that would help.

@Emantor Emantor added this to the v26.1 milestone Feb 18, 2026
Comment thread labgrid/util/marker.py Outdated
Comment on lines +20 to +23
"""labgrid Wrapper of the pexpect module.

This class provides pexpect functionality for the ConsoleProtocol classes.
driver: ConsoleProtocol object to be passed in

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should explain why we need this in addition to the PtxExpect wrapper in labgrid/util/expect.py. It seems this allows the user to use expect only on the process output, without having to care about the markers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread labgrid/util/marker.py Outdated
Comment on lines +31 to +32
def send(self, s):
raise NotImplementedError("Sending not implemented")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we don't want to implement send?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No

Allowing the class to take an integer for a timeout instead of a strict
float seems more convenient to users than forcing it at a higher level,
or breaking usage by changing other APIs to require floats (i.e.
run_check())

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Extends the command protocol to support "background" processes.

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Adds a read mix in class that implements read helper functions to deal
with reading from sources that will return only buffered data, instead
of trying harder to read up to the user requested size.

These functions are modeled after the functions provided by Rust

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Many different drivers need to implement "marker" style command
processes where the command output is terminated with a random marker
then the command status code and a prompt. Instead of making each driver
implement this logic over and over again, implement a common class that
handles this type of command output.

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
@JoshuaWatt JoshuaWatt force-pushed the command-background branch 2 times, most recently from c686d51 to 8e510d5 Compare February 19, 2026 23:50
Implements the start_process() API for "background" processes using the
ConsoleMarkerProcess helper class. Rework the _run() API to use
a background process behind the scenes

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Implements the start_process() API for "background" processes using the
ConsoleMarkerProcess helper class. Rework the _run() API to use
a background process behind the scenes

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
[rouven.czerwinski@linaro.org: adjusted to UBoot timestamp removal]
Signed-off-by: Rouven Czerwinski <rouven.czerwinski@linaro.org>
Implements the start_process() API for "background" processes using the
ConsoleMarkerProcess helper class. Rework the _run() API to use
a background process behind the scenes

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Implements the start_process() API for SSH connections. The
SSHDriverProcess is a wrapper around pexpect.spawn()

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Instructs ssh to open a pseudo terminal when running start_process()
commands.  This allows signals to propagate to the remote process
(instead of being intercepted by the ssh client process), emulating the
start_process() signal behavior on other drivers.

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Disables echo in the shell when using start_process() with the ssh
driver. This prevents duplicate output so it behaves as the other
drivers.

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
@JoshuaWatt

Copy link
Copy Markdown
Contributor Author

Would someone mind looking at this change again? Thanks

@Emantor Emantor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave this a once over and everything seems to be addressed. We just had a release, so its a good time to get this in for additional testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants