Skip to content

Fix: Route existing nodes through updateNode to prevent configuration…#26933

Open
mehtahet619 wants to merge 2 commits into
jenkinsci:masterfrom
mehtahet619:fix-issue-26692
Open

Fix: Route existing nodes through updateNode to prevent configuration…#26933
mehtahet619 wants to merge 2 commits into
jenkinsci:masterfrom
mehtahet619:fix-issue-26692

Conversation

@mehtahet619

Copy link
Copy Markdown

Fixes #26692

Testing done

Manually verified the logic path. Previously, calling addNode() with an existing node name would silently overwrite the Node configuration in the internal ConcurrentMap without notifying the active Computer object. This resulted in the running agent caching old configurations (such as SSHLauncher parameters) until a manual restart.

By delegating existing nodes to the dedicated updateNode() method, the Computer object properly evaluates the new configuration and reconnects if necessary to sync the live runtime state with the updated properties.

Screenshots (UI changes only)

Before

N/A (Core logic change)

After

N/A

Proposed changelog entries

  • Fix configuration desync when programmatically updating existing nodes by ensuring addNode triggers the proper runtime update lifecycle.

Proposed changelog category

/label bug

Proposed upgrade guidelines

N/A

Submitter checklist

  • The issue, if it exists, is well-described.
  • The changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developers, depending on the change) and are in the imperative mood.
  • There is automated testing or an explanation as to why this change has no tests.
  • New public classes, fields, and methods are annotated with @Restricted or have @since TODO Javadocs, as appropriate.
  • New deprecations are annotated with @Deprecated(since = "TODO") or @Deprecated(forRemoval = true, since = "TODO"), if applicable.
  • UI changes do not introduce regressions when enforcing the current default rules of Content Security Policy Plugin.
  • For dependency updates, there are links to external changelogs and, if possible, full differentials.
  • For new APIs and extension points, there is a link to at least one consumer.

Desired reviewers

@jenkinsci/core-pr-reviewers

Copilot AI review requested due to automatic review settings June 15, 2026 10:08
@comment-ops-bot comment-ops-bot Bot added bug For changelog: Minor bug. Will be listed after features labels Jun 15, 2026

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adjusts Nodes#addNode to treat adding a node with an already-existing name as an update rather than an add, aiming to route through the “update” flow for existing nodes.

Changes:

  • Added an early check for existing node names in addNode.
  • Routed existing-name additions to updateNode(node) and returned early.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +183 to +187
// THE FIX: Route to the proper update flow if it already exists
if (nodes.containsKey(node.getNodeName())) {
updateNode(node);
return;
}
Comment on lines +184 to +187
if (nodes.containsKey(node.getNodeName())) {
updateNode(node);
return;
}
Jenkins.checkGoodName(node.getNodeName());
}

// THE FIX: Route to the proper update flow if it already exists
updateNode(node);
return;
}

return;
}
// If a node with this name exists, treat this as an update to preserve update semantics
updateNode(node);

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.

Not sure but is calling updateNode really fixing the issue?

Shouldn't that call replaceNode(old, node)

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.

Taking a closer look, I think that this will also not fix the issue.

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.

Best to add a unit test that shows that the problem is fixed

Copilot AI review requested due to automatic review settings June 15, 2026 19:14

Copilot AI 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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

Comment on lines +184 to +193
Node old = nodes.putIfAbsent(node.getNodeName(), node);

if (old != null) {
if (node == old) {
return;
}
// Use replaceNode as suggested by maintainers to ensure deep configuration sync
replaceNode(old, node);
} else {
handleAddedNode(node, null);
Comment on lines +184 to +191
Node old = nodes.putIfAbsent(node.getNodeName(), node);

if (old != null) {
if (node == old) {
return;
}
// Use replaceNode as suggested by maintainers to ensure deep configuration sync
replaceNode(old, node);
if (node == old) {
return;
}
// Use replaceNode as suggested by maintainers to ensure deep configuration sync
@mawinter69

Copy link
Copy Markdown
Contributor

After the comment #26692 (comment) it is clear that we run into the case node == old being true, so that change will not work in any way.

@mawinter69

Copy link
Copy Markdown
Contributor

You will not be able to solve the problem by calling updateNode anywhere. You must find a path so that Computer#setNode is called, which neither handleAddedNode nor updateNode do.
What will work probably is adding

        if (node == old) {
            Jenkins j = Jenkins.get();
            j.updateComputers(node);
        }

Copilot AI review requested due to automatic review settings June 16, 2026 05:08

Copilot AI 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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment on lines +184 to +191
Node old = nodes.put(node.getNodeName(), node);
if (node != old) {
handleAddedNode(node, old);
} else {
// Fixes #26692: Explicitly update the active Computer so it picks up
// the mutated configuration when the exact same instance is re-added.
Jenkins j = Jenkins.get();
j.updateComputers(node);
if (node != old) {
handleAddedNode(node, old);
} else {
// Fixes #26692: Explicitly update the active Computer so it picks up
Copilot AI review requested due to automatic review settings June 16, 2026 12:56

Copilot AI 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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment on lines +184 to +193
// Array used to safely capture the existing node from inside the lambda
final Node[] previousNode = new Node[1];

// Atomic swap: The map is locked ONLY for this tiny operation
nodes.compute(node.getNodeName(), (name, existingNode) -> {
previousNode[0] = existingNode;
return node;
});

Node old = previousNode[0];
Comment on lines +187 to +188
// Atomic swap: The map is locked ONLY for this tiny operation
nodes.compute(node.getNodeName(), (name, existingNode) -> {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug For changelog: Minor bug. Will be listed after features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Creating a new Jenkins node programatically does not replace completely existing node

3 participants