Skip to content

SED-3310 Local execution of AP should spawn and execute keywords on actual agents - #690

Open
david-stephan wants to merge 31 commits into
masterfrom
SED-3310-local-execution-of-ap-should-spawn-and-execute-keywords-on-actual-agents
Open

SED-3310 Local execution of AP should spawn and execute keywords on actual agents#690
david-stephan wants to merge 31 commits into
masterfrom
SED-3310-local-execution-of-ap-should-spawn-and-execute-keywords-on-actual-agents

Conversation

@david-stephan

Copy link
Copy Markdown
Contributor

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 cl-exense left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You'll want to log file.toAbsolutePath() just to be sure.

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

// 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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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();
    }
}

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.

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use .toAbsolutePath() to be sure.

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

private List<String> vmArgs() {
return Optional.ofNullable(configuration.getJavaAgentVmArgs())
.filter(args -> !args.isBlank())
.map(args -> List.of(args.trim().split("\\s+")))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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/*"

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.

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.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

  1. clearly know what wants to run (e.g. ["java", ".net"]). It that's empty, then nothing needs to be spawned.
  2. 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.

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.

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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

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.

refactored

private String includeCategories;
private String excludeCategories;
private Map<String, String> executionParameters;
public step.agents.provisioning.local.LocalAgentProvisioningConfiguration localAgentConfiguration;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

needlessly fully-qualified, I suppose?

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.

indeed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hehehe, thanks for the acknowledgement. Then why keep it? ;-)

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.

Well I did not just all occurrences :-), updated now

@@ -24,15 +24,39 @@ public enum OperationMode {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 cl-exense left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

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.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

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.

see previous comment

private String includeCategories;
private String excludeCategories;
private Map<String, String> executionParameters;
public step.agents.provisioning.local.LocalAgentProvisioningConfiguration localAgentConfiguration;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hehehe, thanks for the acknowledgement. Then why keep it? ;-)

cl-exense
cl-exense previously approved these changes Sep 1, 2026
cl-exense
cl-exense previously approved these changes Sep 4, 2026

@cl-exense cl-exense left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Loked over the latest changes; while I trust you to better understand the full details, I don't see any glaring problems.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants