Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testrunner: stream output from subprocesses #680

Merged
merged 2 commits into from Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions ci/infra/testrunner/testrunner
Expand Up @@ -22,6 +22,5 @@ setup_python_env() {
}

setup_python_env
# use unbuffered output
stdbuf -i0 -o0 -e0 python -u ${SDIR}/testrunner.py "$@"
Copy link
Contributor

Choose a reason for hiding this comment

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

Ugly as it is, I think we might like to leave the stdbuf commands. There's some desire (SUSE/avant-garde#670) to have tests running within Jenkins or similar generate synchronous output. Imagine something like wget which prints progress output before a newline happens, for example. That, of course, will also require a tweak to how the read_fd threads read from the pipe, but we can deal with that later. This gets us 90% there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would like to only keep things we really need, otherwise we can just keep certain changes because they were there before without a real use. Honestly, if we need to have a complete unbuffered output let's do that in the future, in my opinion.

python -u ${SDIR}/testrunner.py "$@"
deactivate
35 changes: 29 additions & 6 deletions ci/infra/testrunner/utils/utils.py
Expand Up @@ -6,6 +6,7 @@
import subprocess
from functools import wraps
from tempfile import gettempdir
from threading import Thread

import requests
from timeout_decorator import timeout
Expand Down Expand Up @@ -193,16 +194,25 @@ def runshellcommand(self, cmd, cwd=None, env={}, ignore_errors=False):
else:
logger.info("Executing command {}".format(cmd))

p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True,
env=env, cwd=cwd)
logger.debug(p.stdout.decode())
logger.error(p.stderr.decode())
stdout, stderr = [], []
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=env, cwd=cwd)
stdoutStreamer = Thread(target = self.read_fd, args = (p, p.stdout, logger.debug, stdout))
stderrStreamer = Thread(target = self.read_fd, args = (p, p.stderr, logger.error, stderr))
stdoutStreamer.start()
stderrStreamer.start()
stdoutStreamer.join()
stderrStreamer.join()
# this is redundant, at this point threads were joined and they waited for the subprocess
# to exit, however it should not hurt to explicitly wait for it again (no-op).
p.wait()
stdout, stderr = "".join(stdout), "".join(stderr)

if p.returncode != 0:
if not ignore_errors:
raise RuntimeError("Error executing command {}".format(cmd))
else:
return p.stderr.decode()
return p.stdout.decode()
return stderr
return stdout

def ssh_sock_fn(self):
"""generate path to ssh socket
Expand All @@ -221,6 +231,19 @@ def ssh_sock_fn(self):
raise Exception(f"Socket path '{path}' len {len(path)} > {maxl}")
return path

def read_fd(self, proc, fd, logger_func, output):
"""Read from fd, logging using logger_func

Read from fd, until proc is finished. All contents will
also be appended onto output."""
while True:
contents = fd.readline().decode()
if contents == '' and proc.poll() is not None:
return
if contents:
output.append(contents)
logger_func(contents.strip())

@timeout(60)
@step
def setup_ssh(self):
Expand Down
3 changes: 1 addition & 2 deletions ci/jenkins/pipelines/prs/helpers/pr-manager
Expand Up @@ -22,6 +22,5 @@ setup_python_env() {
}

setup_python_env
# use unbuffered output
stdbuf -i0 -o0 -e0 python -u ${SDIR}/pr_manager/pr_manager.py "$@"
python -u ${SDIR}/pr_manager/pr_manager.py "$@"
deactivate