SED-3310 Local execution of AP should spawn and execute keywords on actual agents - #690
Conversation
…should-spawn-and-execute-keywords-on-actual-agents
There was a problem hiding this comment.
Code Review
This pull request introduces a new module, step-agent-provisioning-local, enabling local execution of automation packages by running real Java, Node.js, and .NET agents as separate processes on the developer's machine. Key additions include an embedded execution grid, local agent providers, script engine library extraction, and CLI integration. The code review identifies several critical improvements: resolving a potential NullPointerException in LocalTokenSelectionCriteriaFilter, addressing concurrency and resource leak race conditions in LocalProcessAgentProvisioningDriver during agent provisioning and teardown, refactoring the blocking I/O output pump in LocalAgentProcess from the common ForkJoinPool to a dedicated daemon thread, and adding a fallback for non-atomic moves in ScriptEngineLibraries to support diverse file systems.
cl-exense
left a comment
There was a problem hiding this comment.
Approving with a few comments. In general, that's very solid and well-documented code, so most comments are more about understandability etc. than actual bugs (IIRC), and only very few of them are actual requests to change something.
| } | ||
| logger.debug("Making {} executable", file); | ||
| if (!file.toFile().setExecutable(true)) { | ||
| logger.warn("Unable to make {} executable. Run 'chmod +x' on it if the agent fails to start.", file); |
There was a problem hiding this comment.
You'll want to log file.toAbsolutePath() just to be sure.
| // The directory is named after the version, which does not change between two builds of the same one. An | ||
| // extracted agent is therefore only reused when it is also the same size as the embedded one, otherwise | ||
| // every CLI rebuilt during development would go on running the agent of the first build, for ever. | ||
| long embeddedSize = embeddedAgentSize(); |
There was a problem hiding this comment.
This is not guaranteed to never produce "false negatives". While it's unlikely that the size remains the same over different builds, it's not impossible. I suggest to check the size AND the checksum to minimize the risk; the overhead will be extremely low (compared to everything else we're doing). Here's some simple code:
public long checksum(InputStream is) throws Exception {
try (CheckedInputStream checkedStream = new CheckedInputStream(is, new CRC32C())) {
checkedStream.transferTo(OutputStream.nullOutputStream());
return checkedStream.getChecksum().getValue();
}
}
There was a problem hiding this comment.
refactored to use the checksum available in the JAR (embedded) and storing the current one (extracted) on disk. Note that this only required for local dev, so we should not over engineer and limit the impact on the production code
| try { | ||
| return Files.size(file); | ||
| } catch (IOException e) { | ||
| logger.debug("Unable to determine the size of {}", file, e); |
There was a problem hiding this comment.
Use .toAbsolutePath() to be sure.
| private List<String> vmArgs() { | ||
| return Optional.ofNullable(configuration.getJavaAgentVmArgs()) | ||
| .filter(args -> !args.isBlank()) | ||
| .map(args -> List.of(args.trim().split("\\s+"))) |
There was a problem hiding this comment.
This is reasonable, but still dangerous. Are we sure that individual arguments inside the args String can never contain spaces? (Contrived example for a shell: java -cp "C:/Program Files/crash boom bang/*"
There was a problem hiding this comment.
changed to a support a list of VM arg options to avoid the list of splits all together
| if (requiredAgentTypes.isEmpty()) { | ||
| // Either the keywords are unknown at this point, or none of them declares an agent type this machine can | ||
| // serve. Starting everything available is the only answer left which lets the execution run at all. | ||
| logger.debug("No agent type could be derived from the keywords. Starting one agent of each available type."); |
There was a problem hiding this comment.
Is that a meaningful tactic, i.e. does it actually solve the problem? IOW: is "we don't know the keywords that will run" a meaningful and expected situation?
I would expect to
- clearly know what wants to run (e.g. ["java", ".net"]). It that's empty, then nothing needs to be spawned.
- clearly know what from that list can be run (i.e. if no .net is installed, then it's simply impossible). If that's empty, there's still no need to spawn anything, because it won't help solve the problem anyway.
There was a problem hiding this comment.
This path was incorrect, updated
| /** | ||
| * The conversion words of logback's exception converters, {@code %ex} and {@code %throwable} with their variants | ||
| */ | ||
| private static final List<String> EXCEPTION_CONVERSION_WORDS = List.of("%ex", "%exception", "%throwable", |
There was a problem hiding this comment.
The entire logger handling looks a bit like voodoo. I'm fine with keeping it, but suggest to move it into a separate utility class focused on that functionality, if possible.
| private String includeCategories; | ||
| private String excludeCategories; | ||
| private Map<String, String> executionParameters; | ||
| public step.agents.provisioning.local.LocalAgentProvisioningConfiguration localAgentConfiguration; |
There was a problem hiding this comment.
needlessly fully-qualified, I suppose?
There was a problem hiding this comment.
Hehehe, thanks for the acknowledgement. Then why keep it? ;-)
There was a problem hiding this comment.
Well I did not just all occurrences :-), updated now
| @@ -24,15 +24,39 @@ public enum OperationMode { | |||
|
|
|||
There was a problem hiding this comment.
General note on this class - both the constants and method names are rather confusing, even with the comments. It all sounds like it defines exceptions on top of exceptions on top of exceptions :-)
I can't really think of much better names off the top of my head, but we can have a short discussion/brainstorming if needed.
cl-exense
left a comment
There was a problem hiding this comment.
Two small nitpicks remaining.
|
|
||
| // all parameters | ||
| int res = runMain(histories, "ap", "execute", "-p=src/test/resources/samples/step-automation-packages-sample1.jar", "--local", "--includePlans=p1,p2", "--excludePlans=p3,p4", "--includeCategories=CatA,CatB", "--excludeCategories=CatC,CatD", "-ep=key1=value1|key2=value2", "-ep=key3=value3"); | ||
| int res = runMain(histories, "ap", "execute", "-p=src/test/resources/samples/step-automation-packages-sample1.jar", "--local", "--includePlans=p1,p2", "--excludePlans=p3,p4", "--includeCategories=CatA,CatB", "--excludeCategories=CatC,CatD", "-ep=key1=value1|key2=value2", "-ep=key3=value3", "--localAgentVmArgs=-Xmx4g", "--localAgentVmArgs=-XX:HeapDumpPath=C:\\Program Files\\dumps"); |
There was a problem hiding this comment.
This is a bad idea and is not platform-agnostic. It uses paths that will not be present on most systems (including windows, I don't know how it would behave there), and it would create a leftover directory (literally named "C:\Program Files\dumps") on non-windows machines. I assume that under normal circumstances we don't expect this directory to be used because we don't expect heap dumps, but still...
If this is absolutely required under some circumstances for debugging or so, I suggest to keep the relevant definitions as comments so they can easily be enabled when needed - but not as a default definition that will be used on normal test runs.
There was a problem hiding this comment.
This works anywhere: 1. This test does not cover actual execution which is mocked, 2. the VM arg is HeapDumpPath which does nothing unless a heap dump is triggered. This test covers among other things that such arguments (including backslahes and spaces) are properly handled.
There was a problem hiding this comment.
In that case, fine with me.
| Assert.assertEquals("CatC,CatD", usedParams.excludeCategories); | ||
| Assert.assertEquals(Map.of("key1", "value1", "key2", "value2", "key3", "value3"), usedParams.executionParameters); | ||
| Assert.assertEquals("step-automation-packages-sample1.jar", usedParams.apFile.getName()); | ||
| Assert.assertEquals(List.of("-Xmx4g", "-XX:HeapDumpPath=C:\\Program Files\\dumps"), |
There was a problem hiding this comment.
Related to above comment.
If the purpose of the argument is actually purely for tests, then simply use a relative directory name like "./threaddumps" that's not OS-specific.
There was a problem hiding this comment.
see previous comment
| private String includeCategories; | ||
| private String excludeCategories; | ||
| private Map<String, String> executionParameters; | ||
| public step.agents.provisioning.local.LocalAgentProvisioningConfiguration localAgentConfiguration; |
There was a problem hiding this comment.
Hehehe, thanks for the acknowledgement. Then why keep it? ;-)
…n-and-execute-keywords-on-actual-agents
…gent" This reverts commit dd28136.
cl-exense
left a comment
There was a problem hiding this comment.
Loked over the latest changes; while I trust you to better understand the full details, I don't see any glaring problems.
…n-and-execute-keywords-on-actual-agents
No description provided.