Issue type: Bug report
My Rundeck detail
We are using rundeck for backups - node attribute _services_ describes the services to stop/start during a backup run.
node1:
backupdirs: /etc:/var:/root:/srv
services: httpd:postgresql
node2:
backupdirs: /etc:/var:/root
Expected Behavior
Starting a job with an argument like --services "${node.services}" should give the emtpy string for the services argument on node2
Actual Behavior
If node2 is running after node1, node2 suddenly has a spurious attribute services with value "httpd:postgresql" - since rundeck 2.9.3
With rundeck 2.8.2 the value of the missing node attribute services is ${node.services}
Also not really what you expect, but at least you can detect that this node attribute is actually emtpy. I would say the correct value for missing node attributes should be the empty string ""
How to reproduce Behavior
Create two nodes like above, run a job and pass the node attributes as arguments to the job. Printing out the arguments will give you erratic values for missing node attributes depending on the execution order of the jobs.
Issue rundeck#2756 could be related tho this bug.
Adding to this that this is affecting our deployments too. I created a minimal repro case (before seeing this!), so adding it here in case it is useful.
Steps:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<node name="RDHOST" description="Rundeck server node" tags="" hostname="RDHOST" username="rundeck"/>
<node name="NODE1@HOST"
description="NODE1"
tags="has_properties"
hostname="HOST"
username="NODE1">
<attribute name="Test:Attr1" value="NODE1 Attr1" />
<attribute name="Test:Attr2" value="NODE1 Attr2" />
<attribute name="Test:Attr3" value="NODE1 Attr3" />
</node>
<node name="NODE2@HOST"
description="NODE2"
tags="does_not_have_properties"
hostname="HOST"
username="NODE2">
<attribute name="Test:Attr1" value="NODE2 Attr1" />
<attribute name="Test:Attr3" value="NODE2 Attr3" />
</node>
</project>
- description: Test node variables
executionEnabled: true
group: AutoDeploy
id: fde0e8d5-7d16-4c43-8191-4273c8dab7f3
loglevel: INFO
multipleExecutions: true
name: 'Test: Rundeck node variables'
nodeFilterEditable: false
nodefilters:
dispatch:
excludePrecedence: true
keepgoing: false
rankOrder: ascending
successOnEmptyNodeFilter: false
threadcount: 4
filter: 'tags: has_properties,does_not_have_properties'
nodesSelectedByDefault: true
scheduleEnabled: true
sequence:
commands:
- script: |-
#!/bin/env bash
echo "Server is: |@node.Test:Attr2@|"
keepgoing: false
strategy: node-first
uuid: fde0e8d5-7d16-4c43-8191-4273c8dab7f3
Expected output:
NODE1@HOST: Server is: |NODE1 Attr2|
NODE2@HOST: Server is: ||
Actual Output:
NODE1@HOST: Server is: |NODE1 Attr2|
NODE1@HOST: Server is: |NODE1 Attr2|
Going out on a limb here, I have a suspicion that what's happening is that the node attributes are finding their way into the sharedDataContext due to the call to sharedDataContext.merge(...) here:
public Builder nodeContextData(INodeEntry node) {
// clear any existing node context that might have been left over from
// godknowswhere
ctx.dataContext.remove("node");
ctx.dataContext.merge(new BaseDataContext("node", DataContextUtils.nodeData(node)));
ctx.sharedDataContext.merge(
ContextView.node(node.getNodename()),
new BaseDataContext("node", DataContextUtils.nodeData(node))
);
return this;
}
The merge operation will end up with the superset of all attributes:
@Override
public void merge(final K k, final D data) {
if (data == null) {
throw new NullPointerException("data");
}
if (map.containsKey(k)) {
map.get(k).merge(data);
} else {
map.put(k, data);
}
}
Then I feel like it's _possible_ that leads to SharedDataContextUtils.java:replaceDataReferences to find the node attribute _after_ the DataContextUtil one has had a go (not finding it, as it is created fresh each time).
I'm not very clued up on the codebase, so that could all just be utter rubbish, but it seems plausible. Don't know what the resolution is, mind, but I spent some time looking at it. HTH
thanks @puremourning I think it may be something like that, I plan to investigate this shortly
Thanks for the fix!
I was in the right ballpark, but playing a different ballgame. So it was a shallow copy after all!