1
votes

I defined environment attributes in an environment file, used to override the default settings in a node. In my case, I would like to update an environment attribute in a recipe so when running chef-client on b_node_name the environment attribute will change from this:

default_attributes "hosts" => ["a_node_name"]

to this:

default_attributes "hosts" => ["a_node_name", "b_node_name"]

Doing this I want the new b_node_name where I ran chef-client added to the array so all nodes will get the updated list for new chef-client runs.

Is this possible to do? Should I be using data bags instead to make all nodes read and update that info from there? I'm looking for the best approach to implement this. I would really apprecciatte much any help.

UPDATE: I added the following lines to my recipe:

Chef::Log.info("---> Before update. hosts=#{node.hosts}")
node.override['hosts'] = ["a_hostname", "b_hostname"] 
Chef::Log.info("---> After update. hosts=#{node.hosts}")

And I'm getting this output:

First chef-client run in b_hostname:

#chef-client --log_level info

[2015-07-29T14:29:13+00:00] INFO: ---> Before update. hosts=["a_hostname"]
[2015-07-29T14:29:13+00:00] INFO: ---> After update. hosts=["a_hostname", "b_hostname"]

Second chef-client run in b_hostname:

#chef-client --log_level info
[2015-07-29T14:30:33+00:00] INFO: ---> Before update. hosts=["a_hostname"]
[2015-07-29T14:30:33+00:00] INFO: ---> After update. hosts=["a_hostname", "b_hostname"]

So the hosts value that b_hostname reads is hosts=["a_hostname"] and I want that to be hosts=["a_hostname", "b_hostname"] for the second run.

If I go through Chef Server UI to the node and check hosts attribute I see hosts set to ["a_hostname", "b_hostname"] instead of hosts=["a_hostname"]

Why am I getting hosts=["a_hostname"] during chef-client run?

UPDATE 2: Using node.set instead of node.default or node.override makes the value to persist but just for the node where I ran chef-client (not all nodes) so the above scenario works but not the following one:

First chef-client run in c_hostname:

[2015-07-29T14:58:51+00:00] INFO: ---> Before update. hosts=["a_hostname"]
[2015-07-29T14:58:51+00:00] INFO: ---> After update. hosts=["a_hostname", "b_hostname"]

Where I want to see hosts array set to ["a_hostname", "b_hostname"] done by chef-client run in b_hostname

3

3 Answers

1
votes

UPDATED to allow sorting by time added.

you need to use search. Rather than use an attribute to determine the list, use an attribute to indicate a given node belongs in the list.

node.set['include_me'] = Time.now

Then search for all nodes that have set this, sort them, and get their fqdn:

sorted = search(:node, 'include_me:*').sort_by { |node| node['include_me'] }
hostnames = sorted.map{ |node| node.fqdn }

This will give you an array of fqdns hostnames that is sorted by the added date. One caveat, it will only include nodes that have finished their run. So if you have nodes building in parallel, they will not see one another. Furthermore, the current node will not show up on its first run. So you'll need a little more logic to search for the current node and then add it to the list if it is missing.

1
votes

Chef resets the attributes every single run so the behavior you are seeing is intended - check https://docs.chef.io/attributes.html

If you want to persist a node attribute change between runs use the node.set - in your case

node.set['hosts'] = ['a_node_name', 'b_node_name']

Be careful when changing node attributes during chef client runs - chef client first compiles the attributes to predetermine node attribute values then runs the recipes so if you want to dynamically change the value during run time you might need to put the node.set in a ruby block

0
votes

In the recipe, do this to dynamically changing the value of the attribute.

node.default['hosts'] = ['a_node_name', 'b_node_name']