How to include_recipe inside a ruby_block of a chef recipe
Asked Answered
M

3

7

I have a recipe that sets a variable inside a ruby_block and needs to use that variable as an input attribute for a recipe. How can I use the include_recipe after the ruby_block has been executed?

Thanks

ruby_block "evaluate_config" do #~FC014
 block do

  file = File.read('/opt/config/baselogging.json')
  data = JSON.parse(file)

  node.default['kibana']['apache']['basic_auth_username'] = data['KibanaUser']
  node.default['kibana']['apache']['basic_auth_password'] = data['KibanaPassword']

  include_recipe 'kibana'

 end
end
Magnitude answered 3/3, 2015 at 19:24 Comment(4)
Please look at encrypted data bag which will have encrypted password. ruby_block is part of code that is executed after Resource Collecting so simply put include_recipe outside this block as next step.Serrate
This won't work. include_recipe will be instantiated during the compile phase and default basic_auth_username and basic_auth_password values will be used. On convergence phase the ruby_block will be executed and the values will be set but will have no impact on the included_recipe.Magnitude
What happens when you run the code as you have it? Does it throw an error? You may need to include the Chef recipe code into your ruby block.Grefer
@Magnitude Could you accept my answer, please?Landlady
L
14

To include a recipe from a ruby_block, you must call it using run_context.

For example:

ruby_block "evaluate_config" do #~FC014
 block do
   ...
   #include_recipe 'kibana'
   run_context.include_recipe "cookbook::recipe"
 end
end
Landlady answered 26/3, 2015 at 15:44 Comment(3)
Can you explain your answer please ?Greig
To include a recipe from a ruby_block, you must call it using run_context.Landlady
In the answer itself, it's better ;)Greig
D
1

You can read and set attributes from ruby block, and than after it you can include recipe like:

ruby_block "evaluate_config" do #~FC014
 block do   
  file = File.read('/opt/config/baselogging.json')
  data = JSON.parse(file)

  node.set['kibana']['apache']['basic_auth_username'] = data['KibanaUser']
  node.set['kibana']['apache']['basic_auth_password'] = data['KibanaPassword']   
 end
end

include_recipe 'kibana'
Degeneracy answered 4/3, 2015 at 8:29 Comment(4)
This won't work. include_recipe will be instantiated during the compile phase and default basic_auth_username and basic_auth_password values will be used. On convergence phase the ruby_block will be executed and the values will be set but will have no impact on the included_recipe.Magnitude
Ohh i see, i have found this gist.github.com/arangamani/4659646 it seems to be related with your problemDegeneracy
Lazya attribute evaluation is great option too docs.chef.io/resource_common.html#lazy-attribute-evaluationDegeneracy
The problem is that I shouldn't have to modify the 'kibana' recipe to specify a lazy attribute wherever it's used or know the internals to use the run_context.resource_collection.find(...). Recipes should be independentMagnitude
A
0

It looks like the issue here is that the kibana cookbook has a default recipe which does not have lazy {} modifiers around its use of the node['kibana']['apache']['basic_auth_username'] and password node attributes.

It looks like there's a lot of work being done here to lazy everything and use a ruby_block for reasons that I don't understand. A better approach would be to simply not use a ruby_block:

file = File.read('/opt/config/baselogging.json')
data = JSON.parse(file)

node.default['kibana']['apache']['basic_auth_username'] = data['KibanaUser']
node.default['kibana']['apache']['basic_auth_password'] = data['KibanaPassword']

include_recipe 'kibana'

If chef itself is responsible for generating baselogging.json and you're trying to generate baselogging.json and then read from baselogging.json the solution I'd come up with would be to refactor and remove that:

data = ... stuff to populate the data ...

file "/opt/config/baselogging.json" do
  content JSON.generate(data)
end

[...]

node.default['kibana']['apache']['basic_auth_username'] = data['KibanaUser']
node.default['kibana']['apache']['basic_auth_password'] = data['KibanaPassword']

include_recipe 'kibana'

Even if there's currently a remote_file resource somewhere that creates baselogging.json you'd be better off doing something like this:

# "cheat" and download the file at compile-time
remote_file "/opt/config/baselogging.json" do
   source "http://example.org/baselogging.json"
   action :nothing
end.run_action(:create)

file = File.read('/opt/config/baselogging.json')
data = JSON.parse(file)

node.default['kibana']['apache']['basic_auth_username'] = data['KibanaUser']
node.default['kibana']['apache']['basic_auth_password'] = data['KibanaPassword']

include_recipe 'kibana'

The larger point here is that lazy {} creates an arms race to lazying actions more and more and if you're consuming cookbooks outside of your control then it becomes uglier and uglier. The whole question has a lot of "code smell" that forcing stuff to happen later and later is winding up fighting how everything has been architected. You're better off going back and refactoring your assumptions so that you can move more work forwards in the chef run.

In general, you would try to compile all the information in your node attributes in the attributes file parsing phase. Setting attributes in recipe code leads to issues like this one and winds up where you find yourself wanting to submit PRs to every community cookbook in existence to lazy all of the attributes that they use. Using resource-driven library cookbooks instead of attribute-and-recipe driver library cookbooks can help sidestep this whole process. Barring that though you should assemble your node data early so that you do not have to lazy all the access to your node data.

If you must construct node data in recipe code then you must construct that data at compile time. Trying to set node data at recipe converge time is a symptom that you've gotten a bit lost.

Antidepressant answered 24/10, 2016 at 22:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.