Rundeck: Missing node attribute is taken from another node

Created on 16 Sep 2017  路  5Comments  路  Source: rundeck/rundeck

Issue type: Bug report

My Rundeck detail

  • Rundeck version: 2.9.3
  • install type: deb
  • OS Name/version: Ubuntu 16.04
  • DB Type/version: mariadb-server-10.0

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.

bug

All 5 comments

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:

  1. Create a new empty project
  2. Add the following resource defintions:
<?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>

  1. Add the following job:
- 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

  1. Run the job

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!

Was this page helpful?
0 / 5 - 0 ratings