Chef - create template with dynamic variable?

谁都会走 提交于 2020-01-01 08:59:11

问题


I'm having a bit of a challenge on a Chef recipe. I'm new to Chef, so please bear with me.

Step 1: My chef recipe installs Ruby Passenger, then compiles the Passenger nginx module along with Nginx.

# Install passenger and nginx module
bash "Install Passenger" do
  code <<-EOF
  source /usr/local/rvm/scripts/rvm
  gem install passenger
  EOF
  user "root"
  not_if { `gem list`.lines.grep(/^passenger \(.*\)/).count > 0 }
end

# Install passenger
# Note that we have to explicitly include the RVM script otherwise it won't setup the environment correctly
bash "Install passenger nginx module and nginx from source" do
  code <<-EOF
  source /usr/local/rvm/scripts/rvm
  passenger-install-nginx-module --auto --prefix=/opt/nginx --auto-download
  EOF
  user "root"
  not_if { File.directory? "/opt/nginx" }
end

Step 2: After that, I create the nginx config file using a template. This configuration requires the location of Passenger, which is dependent on step 1 completing.

template "/opt/nginx/conf/nginx.conf" do
  source "nginx.conf.erb"
  action :create
  variables(
    deploy_user: deploy_user,
    passenger_root: `bash -c "source /usr/local/rvm/scripts/rvm; passenger-config --root"`.chomp,
    passenger_ruby: `bash -c "source /usr/local/rvm/scripts/rvm; which ruby"`.chomp,
    passenger: node[:passenger]
  )
end

Problem: Chef appears to compile templates at th ebeginning of the run. So what ends up happening is that Step 2 is actually compiled before Step 1 is run. This means that the passenger_root variable is blank. It needs Step 1 to complete before being able to get the passenger_root, then run the template.

I tried wrapping the step 2 code in a ruby_block but that doesn't work: undefined methodtemplate' for Chef::Resource::RubyBlock`.

Not sure what to do here, or what is the best practice for Chef for something like this?

Thanks in advance,

Leonard


回答1:


A cleaner and recommended way is to use Lazy Attribute Evaluation.

template "/opt/nginx/conf/nginx.conf" do
  source "nginx.conf.erb"
  action :create
  variables lazy {
    { 
      deploy_user: deploy_user,
      passenger_root: `bash -c "source /usr/local/rvm/scripts/rvm; passenger-config --root"`.strip,
      passenger_ruby: `bash -c "source /usr/local/rvm/scripts/rvm; which ruby"`.strip,
      passenger: node[:passenger]
    }
  }
end

Also, I'd suggest using strip instead of chomp [thanks Draco].




回答2:


As soon as you wrap your code in ruby_block you cannot use ordinary recipe resource declaration anymore. You have to write pure ruby code:

ruby_block "create /opt/nginx/conf/nginx.conf from template" do
  block do
    res = Chef::Resource::Template.new "/opt/nginx/conf/nginx.conf", run_context
    res.source "nginx.conf.erb"
    res.variables(
      deploy_user: deploy_user,
      passenger_root: `bash -c "source /usr/local/rvm/scripts/rvm; passenger-config --root"`.chomp,
      passenger_ruby: `bash -c "source /usr/local/rvm/scripts/rvm; which ruby"`.chomp,
      passenger: node[:passenger]
    )
    res.run_action :create
  end
end

PS. And I guess you want to use strip instead of chomp to remove whitespace.




回答3:


Yeah, Chef is a beast. I think part of the problem is there are a million ways to do the same things but there really is no documentation detailing the best way. What you probably want is to use Notifications, so that the block 1 runs first than notifies the block 2 to run. This means block 2 needs action :none so that it does not trigger until it gets notified.

I added the notify to your example in block 1 and added the action :none to block 2.

bash "Install Passenger" do
  code <<-EOF
  source /usr/local/rvm/scripts/rvm
  gem install passenger
  EOF
  user "root"
  not_if { `gem list`.lines.grep(/^passenger \(.*\)/).count > 0 }
  notifies :run, 'bash[Install passenger nginx module and nginx from source]', :immediately
end

bash "Install passenger nginx module and nginx from source" do
  code <<-EOF
  source /usr/local/rvm/scripts/rvm
  passenger-install-nginx-module --auto --prefix=/opt/nginx --auto-download
  EOF
  user "root"
  action :none
end


来源:https://stackoverflow.com/questions/15743546/chef-create-template-with-dynamic-variable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!