Search code examples
chef-infralazy-evaluationdynamic-variablesconvergence

Chef: Lazy evaluation of variable argument


We have a situation with Mapr client install. We would like to be able to copy a customized hive-site.xml file which resides under /opt/mapr/hive/hive-x.y/conf/ dir. The issue is that we wouldn't know the hive version number until after a set of packages (including hive) are fully installed. So, code like the following is failing:

package 'mapr-client'
...
...
dir_hive = Dir["/opt/mapr/hive/hive-*"]
template "#{dir_hive[0]}/conf/hive-site.xml" do
    source "devg_hive-site.xml.erb"
    mode 0644
end

It appears that the template name is getting evaluated at the compile time at which point the package install is not complete so the name is getting evaluated to /conf/hive-site.xml instead of /opt/mapr/hive/hive-1.2/conf/site.xml. If we wait for all the packages to be completely installed and through a separate invocation or do a manual run of the following code:

dir_hive = Dir["/opt/mapr/hive/hive-*"]
template "#{dir_hive[0]}/conf/hive-site.xml" do
    source "devg_hive-site.xml.erb"
    mode 0644
end

We have successful copy of the config file; but, if it is part of the process where you install and try to configure, it does not work. Here are some of the other things that we tried:

  • Tried to move the config copy part of the code into a separate recipe and created a run list and hoped that if the order of the execution is maintained, once the package install is complete, the hive dir will be available so we will have the right file path. That didn't work.
  • Put the config copy code under ruby_block, lazy evluator only_if (checking for /opt/mpar/hive dir), etc. No luck.
  • Copied the source xml file to a /tmp/ dir attempted to simply copy the file when /opt/mapr/hive dir is available, again using the lazy evaluator, ruby_block, etc. Still fails.
  • Tried to go through a loop in ruby_block trying to check if !Dir.glob('/opt/mapr/hive/hive-*/conf/hive-site.xml').empty? and sleep for 5 seconds. Fails.

Few other variations to the above weren't successful either; in all of these cases, it appears that the "#{dir_hive[0]}/conf/hive-site.xml" is getting evaluated at the compile time and hence yielding the wrong file path.

What is the best way to capture the installed file path using a wild character and then use that path for some operation (such as replace a config file with a custom one).

Alternatively, can we lazy evaluate an expression to yield a variable name after some action is performed and the lazy evaluated variable name depends on a file name that comes into existence after the action?

Thanks for your time and appreciate any pointers!


Solution

  • It's extremely 'smelly' and it'll probably bite you on upgrades. Because newer version will have higher number. So you can try to use last instead of first.

    template "hive-site.xml" do
      path lazy { "#{Dir['/opt/mapr/hive/hive-*'].last}/conf/hive-site.xml" }
      source "devg_hive-site.xml.erb"
    end
    

    I'm not sure how common this is in your code, but you can think about resource/code which will extract path from package (metadata or list files).

    EDIT. I've second thoughts. Package may be recorded in node["packages"] with version which can be mapped to your path, you could use it and drop Dir (you'll still need lazy block), you may need to run ohai plugin to refresh node["packages"] after package installation (I think you can use notifications for this).

    It may look similar to this:

    ohai "reload packages" do
      plugin "packages"
      action :nothing
    end
    
    package "mapr-client" do
      notifies :reload, "ohai[reload packages]", :immediately
    end
    
    template "hive-site.xml" do
      path lazy { "/opr/mapr/hive/hive-#{node["packages"]["mapr-client"]["version"]}/conf/hive-site.xml" }
      source "devg_hive-site.xml.erb"
    end
    

    Unfortunately version probably won't map to directory structure. This package is just bad ;-)