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

Catch silent subproc_execute failures #2404

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

pryce-turner
Copy link
Contributor

Why are the changes needed?

Under some circumstances, subproc_execute will exit with 0 but have a non-null stderr.

What changes were proposed in this pull request?

Adds an assert statement to handle 0 exit with stderr.

How was this patch tested?

Unit test to capture AssertionError and ensure the message contains the right content.

Setup process

make setup
make unit_test

Signed-off-by: pryce-turner <[email protected]>
Copy link

codecov bot commented May 9, 2024

Codecov Report

Attention: Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.

Project coverage is 72.97%. Comparing base (4dd4d22) to head (9132cf3).
Report is 53 commits behind head on master.

Current head 9132cf3 differs from pull request most recent head e2f41dd

Please upload reports for the commit e2f41dd to get more accurate results.

Files Patch % Lines
flytekit/extras/tasks/shell.py 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2404      +/-   ##
==========================================
- Coverage   75.81%   72.97%   -2.84%     
==========================================
  Files         181      215      +34     
  Lines       18327    19541    +1214     
  Branches     2580     3602    +1022     
==========================================
+ Hits        13895    14261     +366     
- Misses       3830     4660     +830     
- Partials      602      620      +18     

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

@pryce-turner pryce-turner changed the title Made outfile ephemeral Catch silent subproc_execute failures May 10, 2024
# This is a corner case I ran into that really shouldn't
# ever happen. The assert catches anything in stderr despite
# a 0 exit.
cmd = " ".join(["bcftools", "isec", "|", "gzip", "-c", ">", f"{tmp_path.joinpath('out')}"])
Copy link
Collaborator

Choose a reason for hiding this comment

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

the exitcode 0 only happens because the second command (i.e. gzip) returns a 0. Bash implements a feature called PIPESTATUS, which captures the exit codes of all commands in a pipe. It's just an array that you can verify after the pipe is run, for example:

bash-5.2$ inexistet command --flag | echo bla | gzip -c > out
bash: inexistet: command not found
bash-5.2$ echo "${PIPESTATUS[@]}"
127 0 0

Notice how the first command returns 127 and the other two return 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TIL, thank you!

@@ -80,6 +80,8 @@ def subproc_execute(command: typing.Union[List[str], str], **kwargs) -> ProcessR
# Execute the command and capture stdout and stderr
result = subprocess.run(command, **kwargs)

assert result.stderr == ""
Copy link
Collaborator

Choose a reason for hiding this comment

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

sometimes commands write debug information to stderr, so we should have a more resilient way of checking if a subprocess ran to completion.

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 wish they didn't but you're right.. unfortunately, subprocess.run doesn't capture PIPESTATUS it seems, only showing the return code for the last command. Do you have any thoughts? The only thing I can think of would be to check stderr for "command not found" or "error", but that's pretty awkward.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unfortunately I don't have a great solution.

pipestatus is a bash feature, so this complicates things a little since we use /bin/sh as the default shell in ShellTask and depending on the OS that's not bash (e.g. on MacOS /bin/sh is symlinked to bash:

❯ uname -a
Darwin gondor 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:31:00 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6020 arm64

❯ readlink /private/var/select/sh
/bin/bash

).

A low-lift solution is to write a warning to the flytekit logs that we detected the use of a pipe in the command and that means we might end up not capturing the output of the command. That's not a great solution either, but at least we let the users know that they are in pipeland, which means they will have to handle it (maybe mention pipestatus?). wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think that's the best solution given the current constraints. It is a bit of a corner case and I don't think we should be expected to handle every failure state. How does the latest commit look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants