Passing variables between chef resources

14,136

Solution 1

You have already solved problem A with the Ruby block.

Now you have to solve problem B with a similar approach:

ruby_block "create user" do
  block do
    user = Chef::Resource::User.new(node[:var], run_context)
    user.shell '/bin/bash' # Set parameters using this syntax
    user.run_action :create
    user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
  end
end

You could also solve problem A by creating the file during the compile phase:

execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
  action :nothing
end.run_action(:run)

If following this course of action, make sure that:

  • /usr/local/bin exist during Chef's compile phase;
  • Either:
    • stuff.sh is executable; OR
    • Execute it through a shell (e.g.: var=`sh /usr/local/bin/stuff.sh`

Solution 2

The modern way to do this is to use a custom resource:

in cookbooks/create_script/resources/create_script.rb

provides :create_script
unified_mode true

property :script_name, :name_property: true

action :run do
  execute "creating #{script_name}" do
    command "echo 'echo stuff' > #{script_name}"
    not_if { File.exist?(script_name) }
  end
end

Then in recipe code:

create_script "/usr/local/bin/stuff.sh"

For the second case as written I'd avoid the use of a node variable entirely:

script_location = "/usr/local/bin/stuff.sh"

create_script script_location

# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location 

If you need to move it into an attribute and call it from different recipes then there's no need for ruby_blocks or lazy:

some cookbook's attributes/default.rb file (or a policyfile, etc):

default['script_location'] = "/usr/local/bin/stuff.sh"

in recipe code or other custom resources:

create_script node['script_location']

user node['script_location']

There's no need to lazy things or use ruby_block using this approach.

Share:
14,136
ShinySpiderdude
Author by

ShinySpiderdude

Updated on June 21, 2022

Comments

  • ShinySpiderdude
    ShinySpiderdude almost 2 years

    i would like to show you my use case and then discuss possible solutions:

    Problem A: i have 2 recipes, "a" and "b".. "a" installs some program on my file system (say at "/usr/local/bin/stuff.sh" and recipe "b" needs to run this and do something with the output.

    so recipe "a" looks something like:

    execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" 
    

    (the script just echo(es) "stuff" to stdout)

    and recipe "b" looks something like:

    include_recipe "a"
    var=`/usr/local/bin/stuff.sh` 
    

    (note the backquotes, var should contain stuff)

    and now i need to do something with it, for instance create a user with this username. so at script "b" i add

    user "#{node[:var]}"
    

    As it happens, this doesn't work.. apparently chef runs everything that is not a resource and only then runs the resources so as soon as i run the script chef complains that it cannot compile because it first tries to run the "var=..." line at recipe "b" and fails because the "execute ..." at recipe a did not run yet and so the "stuff.sh" script does not exist yet. Needless to say, this is extremely annoying as it breaks the "Chef runs everything in order from top to bottom" that i was promised when i started using it. However, i am not very picky so i started looking for alternative solutions to this problem, so:

    Problem B: i've run across the idea of "ruby_block". apparently, this is a resource so it will be evaluated along with the other resources. I said ok, then i'd like to create the script, get the output in a "ruby_block" and then pass it to "user". so recipe "b" now looks something like:

    include_recipe "a"
    
    ruby_block "a_block" do
      block do
        node.default[:var] = `/usr/local/bin/stuff.sh`
      end
    end
    
    user "#{node[:var]}"
    

    However, as it turns out the variable (var) was not passed from "ruby_block" to "user" and it remains empty. No matter what juggling i've tried to do with it i failed (or maybe i just didn't find the correct juggling method)

    To the chef/ruby masters around: How do i solve Problem A? How do i solve Problem B?

  • lamont
    lamont over 3 years
    This solution was probably correct in 2013, but is no longer correct. Even if you need to lazy the username a simple user lazy { node['var'] } will suffice without needing to wrap the entire declaration in a ruby_block, and there's no need to construct resources by hand any more.