0

I am new to chef environment. And working on a simple cookbook.

To simplify, I have following attributes in:

attributes/default.rb:

default[:user1] = ""
default[:user2] = ""
default[:filename] = ""

recipes/default.rb: At run time, I get a filename from another cookbook and I have to extract users from that file.

file = "#{node[:filename]}"

ruby_block 'extract userdata' do
  block do
    json = File.read(file)
    obj = JSON.parse(json)

    userdata = obj['users']

    if userdata.empty?
      raise "Errors: userdata not available"
    else
      node.override[:user1] = userdata['user1']
      node.override[:user2] = userdata['user2']
    end
  end
  puts "user1: #{node[:user1]}"
  puts "user2: #{node[:user2]}"
  action :run
end

logs (puts) from above block at runtime do fetch me usernames correctly.

Now, I am trying to use above two updated attributes as below in further down the cookbook recipe.

user1 = #{node[:user1]}
user2 = #{node[:user2]}

But these two values are coming out to be empty as if they are not set/overriden.

Please suggest how can I get updated data.

Ram
  • 1,153
  • 4
  • 16
  • 34
  • Quite long to explain, but you're hit by the "two pass run" aka "compile vs converge time" problem. Part of your code is run at compile time, whereas other parts are run at converge time. See [this](http://docs.chef.io/chef_client.html#the-chef-client-title-run) for how the run is done. (and do a search with the names I provided, you'll find answers) – Tensibai Aug 17 '15 at 09:02

2 Answers2

2

I'll summarize the following answers (which gives another views on the same problem) and gives an example on your specific case.


You're problem here is compile time versus converge time

What happens here is that the recipe and the resources are evaluated at compile time and the content of your ruby_block resource is evaluated at converge time.

There's 2 solutions to get around this:

  • First option is to run your ruby_block at compile time:

    ruby_block 'extract userdata' do
      block do
        json = File.read(file)
        obj = JSON.parse(json)
    
        userdata = obj['users']
    
        if userdata.empty?
          raise "Errors: userdata not available"
        else
          node.override[:user1] = userdata['user1']
          node.override[:user2] = userdata['user2']
        end
      end
      puts "user1: #{node[:user1]}"
      puts "user2: #{node[:user2]}"
      action :nothing
    end.run_action(:run)
    

    Notice the action :nothing to avoid the resource to be executed at converge time too and the .run_action(:run) which tell the evaluation to run this resource as soon as it's evaluated.

  • Second option is to use lazy evaluation in other resources like this:

    execute "Do something" do
      command lazy { "/path/command #{node['user1]}" }
    end
    

    The lazy {} has to encompass the whole attribute value, not just the variable or it will throw an error.

I advise to preferably use option 2 as much as possible. Doing things at compile time could end up in a difficult run to understand when it goes wrong as actions are taken in two different flows.

I recommend using string to access node attributes, single quoted when fixed text, double quoted when you need interpolation as symbols can get unexpected behaviors sometimes if an attribute name match an existing symbol in the recipe DSL namespace.

Community
  • 1
  • 1
Tensibai
  • 15,557
  • 1
  • 37
  • 57
0

Try add node.save after override/set new values to the attributes in the code block.

if userdata.empty?
  raise "Errors: userdata not available"
else
  node.override[:user1] = userdata['user1']
  node.override[:user2] = userdata['user2']
  node.save
end
display name
  • 4,165
  • 2
  • 27
  • 52
  • This won't fix the problem as the resources after will be evaluated at compile time when the block has not been evaluated, so they will still use old values. – Tensibai Aug 21 '15 at 08:42
  • And it's of no use in this case, the `override` method update the node object, the use case to `node.save` is to update the node object on chef-server to be sure it's updated even if the run fail later, this allow using attributes as guards to not redo an install, or in long runs to make the node available in search to other nodes. – Tensibai Aug 21 '15 at 09:04