-
Notifications
You must be signed in to change notification settings - Fork 3
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
Fix LocalExecutionService ignored errors #351
Conversation
… error reporting (via logger). Some smaller fixes and refactorings.
|
||
/** | ||
* The local execution service executes commands on the local machine. For this groovy's execute() method is used. | ||
* @author michael | ||
* All commands are executed using Bash as interpreter! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was not the case before. The change corrects the inconsistent behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a pretty important (breaking?) change.
Should the comment permanently reflect this, e.g. by adding some wording like
prior to version vX.X.X, varying interpreters were used. This was normalised to consistently use
bash
starting at version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I also did not have updated the changelogs. This should be fixed. The choice to do it like this is justified by that for synchronous execution also bash -c is used (via LocalExecutionHelper in RoddyToolLib). Note that Bash is a requirement of Roddy anyway and I know only of executions in Bash environments. So this change is very unlikely to actually break anything.
Note however, that the SSHExecutionService apparently does not wrap everything into Bash. This may also be a good argument for removing the bash -c wrapping from RoddyToolLib. I'm uncertain. From a conceptual perspective the actual execution and the wrapping in Bash are different things and should be dealt with by different classes (e.g. some kind of BashCommand class that could also do basic syntax checks etc.). Also this may be an argument for actually not doing the wrapping at all. Unfortunately, I am not sure whether actually something would break. So wrapping everything into Bash is the safer solution than removing the wrapping from commands that possibly would break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comparison of String.execute()
and bash -c
in a ProcessBuilder
, below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the thumb-up means. "Good discussion"? "Leave it like it is"? "Poor guy. Keep up the good mood!" ;-)
@@ -630,7 +630,7 @@ class FileSystemAccessProvider { | |||
boolean setAccessRightsRecursively(File path, String accessStringDirectories, String accessString, String group) { | |||
return ExecutionService.instance. | |||
execute(commandSet.getSetAccessRightsRecursivelyCommand( | |||
path, accessStringDirectories, accessString, group), false) | |||
path, accessStringDirectories, accessString, group)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will continue to run asynchronously, but with better error reporting.
build.gradle
Outdated
@@ -92,7 +92,7 @@ repositories { | |||
} | |||
|
|||
|
|||
ext.roddyToolLibVersion = "2.1.1" | |||
ext.roddyToolLibVersion = "2.2.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments from yesterday, round 1
Still checking the rest together with @jpalbrecht
@@ -1,81 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consistency: Why delete the .iml
?
Comparing to RoddyToolLib
, there .idea
is gone, but the .iml
is kept.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I did not notice and I am very surprised, because apparently IntelliJ does not need this. The file is gone, but the project module -- now simply imported from the Gradle model -- is present. Not sure yet, what that might mean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am more or less the only developer left and the system needed to be changed, because IntelliJ apparently does not support the old way with one module including sources and tests. All this is now automatically modelled as submodules and there is no way to change this, because IntelliJ automatically reimports from the Gradle-file.
I suggest we leave this like it is here in the PR.
import de.dkfz.roddy.tools.LoggerWrapper | ||
import groovy.transform.CompileStatic | ||
|
||
import java.util.concurrent.Callable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently an unused import, according to IntelliJ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
|
||
/** | ||
* The local execution service executes commands on the local machine. For this groovy's execute() method is used. | ||
* @author michael | ||
* All commands are executed using Bash as interpreter! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a pretty important (breaking?) change.
Should the comment permanently reflect this, e.g. by adding some wording like
prior to version vX.X.X, varying interpreters were used. This was normalised to consistently use
bash
starting at version
RoddyCore/src/de/dkfz/roddy/execution/io/LocalExecutionService.groovy
Outdated
Show resolved
Hide resolved
|
||
def "execute('echo')"() { | ||
when: | ||
ExecutionResult res = es.execute("echo 'hello'", true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency compared to the methods below, should there be a waitFor = true
in the test name (also for the next)
RoddyCore/test/src/de/dkfz/roddy/execution/io/LocalExecutionServiceSpec.groovy
Outdated
Show resolved
Hide resolved
|
||
LocalExecutionService es = ExecutionService.instance | ||
|
||
def "execute('echo')"() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in meeting: these names can be better 😄
import de.dkfz.roddy.tools.LoggerWrapper | ||
import groovy.transform.CompileStatic | ||
|
||
import java.util.concurrent.Callable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
|
||
/** | ||
* The local execution service executes commands on the local machine. For this groovy's execute() method is used. | ||
* @author michael | ||
* All commands are executed using Bash as interpreter! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I also did not have updated the changelogs. This should be fixed. The choice to do it like this is justified by that for synchronous execution also bash -c is used (via LocalExecutionHelper in RoddyToolLib). Note that Bash is a requirement of Roddy anyway and I know only of executions in Bash environments. So this change is very unlikely to actually break anything.
Note however, that the SSHExecutionService apparently does not wrap everything into Bash. This may also be a good argument for removing the bash -c wrapping from RoddyToolLib. I'm uncertain. From a conceptual perspective the actual execution and the wrapping in Bash are different things and should be dealt with by different classes (e.g. some kind of BashCommand class that could also do basic syntax checks etc.). Also this may be an argument for actually not doing the wrapping at all. Unfortunately, I am not sure whether actually something would break. So wrapping everything into Bash is the safer solution than removing the wrapping from commands that possibly would break.
RoddyCore/test/src/de/dkfz/roddy/execution/io/LocalExecutionServiceSpec.groovy
Show resolved
Hide resolved
|
||
/** | ||
* The local execution service executes commands on the local machine. For this groovy's execute() method is used. | ||
* @author michael | ||
* All commands are executed using Bash as interpreter! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…d but doesn't work anymore, but openjdk8 works.
…thub.com/TheRoddyWMS/Roddy into fix-localexecutionservice-ignores-errors
…and composition in BashCommandSet. - Accompanying changes to express optionality of return value in associated methods and handling of these changes. - Removed unnecessarily complicated Optional usage where simple if/else is better - Temporarily set RoddyToolLib dependency to test-release number for testing, until upstream changes are accepted - Fix unpredictable result line order for local synchronous execution tests. Tests were done with with without --include-build parameters for gradle and `gradle dependencies` to check that the right RoddyToolLib was included, to make sure, RTL and BE 0.0.7 play well together.
Whether it generally is a good idea to produce commands (like `rm -rf $file`), where the file can actually be a glob, one can be sceptical about. Some futher comments and small Roddy-internal refactorings.
…cCacheFileCommand. Rename a Spock test file. Otherwise layout and comment changes.
command.execute() | ||
} | ||
return new ExecutionResult(true, 0, [], "") | ||
ProcessBuilder processBuilder = new ProcessBuilder(["bash", "-c", command]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old implementation used String.execute(String)
, which eventually refers to Runtime.exec(String command, String[] end, File dir)
. This latter method tokenizes the command using the delimiters space, tab, newline, carriage-return, form-feed. Bash, by contrast, uses the delimiters space, tab, newline by default (the $IFS
variable), which, however, in this context here boils down to the same, because here only spaces are relevant token-delimiters. This means that in our context, command.execute()
and bash -c ...
called via ProcessBuilder
will do the same.
|
||
/** | ||
* The local execution service executes commands on the local machine. For this groovy's execute() method is used. | ||
* @author michael | ||
* All commands are executed using Bash as interpreter! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comparison of String.execute()
and bash -c
in a ProcessBuilder
, below.
RoddyCore/src/de/dkfz/roddy/execution/io/fs/FileSystemAccessProvider.groovy
Show resolved
Hide resolved
RoddyCore/src/de/dkfz/roddy/execution/io/fs/FileSystemAccessProvider.groovy
Show resolved
Hide resolved
} | ||
|
||
def "execute('echo \"hello\"; false', waitFor = false)"() { | ||
def "execute synchronously and succeed with empty stdout and non!-ignored stderr"() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't that be a "execute synchronously and fail " (exit code = 1)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. Maybe a tabular test would have been more appropriate for all these parameter combinations and results.
RoddyCore/test/src/de/dkfz/roddy/execution/io/LocalExecutionServiceSpec.groovy
Show resolved
Hide resolved
Question: There is a seemingly empty merge-commit in the history. OK either way if you keep or remove. |
RoddyCore/src/de/dkfz/roddy/execution/io/fs/FileSystemAccessProvider.groovy
Show resolved
Hide resolved
@Override | ||
String getCheckChangeOfPermissionsPossibilityCommand(File file, String group) { | ||
File testFile = new File(file, ".roddyPermissionsTestFile") | ||
if (null == group) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is Yoda conditions default in Roddy? Seen this at various positions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I try to use these "Yoda conditions" (thanks for the term) to not by chance write group = null
. Note that group == null
evaluates to true
if group is null, but group = null
evaluates to false
. So, in case of a typo with =
rather than ==
, not only group
would be assigned, but also the other branch would be taken. Just a safety measure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I never checked, but apparently Groovy does not accept assignments in conditions. So it is not strictly unnecessary. However, the "added cognitive load" counter-argument on the Wiki page is ridiculously irrelevant in the context of the overall complexity of the code. So I leave it as it is but avoid it in the future. Thanks for pointing out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pair-reviewed together with @jpalbrecht ;
Good to merge, excluding a few minor remarks, and one incorrect spec-name (copy paste error);
All the other things are basically stylistic things that are also fine as-is.
provider.setDefaultAccessRightsRecursively(new File(analysisToolsServerDir.getAbsolutePath()), context) | ||
boolean success = provider.setDefaultAccessRightsRecursively(new File(analysisToolsServerDir.getAbsolutePath()), context) | ||
if (!success) | ||
logger.warning("Command error occurred and was ignored") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This message is the equivalent of "something went wrong" in it's informativeness.
Alternative suggestion:
"Ignoring command error while setting default access rights on analysisToolsServerDir"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too true to be true.
RoddyCore/src/de/dkfz/roddy/execution/io/ExecutionService.groovy
Outdated
Show resolved
Hide resolved
- recompile for 2.5.9 release including timestamping - some code reformating (to be able to read on my narrow notebook screen) - test class rename to be standard-conform
LocalExecutionService ignored errors when running asynchronously with waitFor=false. This may have been responsible for non-detection of errors during bresume calls and accumulation of PSUPS LSF-cluster-jobs.
This fix uses the AsyncExecutionResult to wrap asynchronously computed exit code, stdout, & stderr. Currently, errors are only reported via log-messages if ignoreErrors = true but produce an UnexpectedExecutionResultException if ignoreErrors = false.
Additional smaller changes are commented in the PR.