根据Chef的两遍模型,在ruby-block,guard块和lazy中的任何代码仅在运行时执行。
我有一个包含三种资源的食谱。
在编译阶段分配值的变量。
require 'socket'
require 'down'
require 'net/http'
conf = `hostname`.chomp
vpn_ip_list = Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }
!vpn_ip_list.empty? ? ip_addr = vpn_ip_list.first.ip_address : ip_addr = ""
第一资源-检查是否使用保护块中的代码分配了所需的VPN IP地址,如果未分配,则下载conf文件并通知第二服务资源重新启动openvpn。保护块内部变量的值是在编译阶段获得的。
ruby_block 'download_openvpn_conf' do
block do
attempt = 2
begin
retries ||= 0
tempfile = Down.download("http://some-url1/#{conf}",max_redirects: 0)
FileUtils.mv tempfile.path, "#{node['openvpn-conf-path']}/#{tempfile.original_filename}"
FileUtils.chmod 0644, "#{node['openvpn-conf-path']}/#{tempfile.original_filename}"
rescue Down::Error => e
node.run_state['error'] = e.to_s
puts e
Chef::Log.warn ("\n \t ***********#{e}***********")
retry if (retries += 1) < 1
end
end
only_if {vpn_ip_list.size.eql?(0) || vpn_ip_list.size >= 2}
action :run
notifies :restart, 'service[openvpn]', :immediately
notifies :delete, "file[#{node['openvpn-conf-path']}/#{conf}]", :before
end
第二资源-重新启动openvpn服务。此时,VPN IP已分配给系统。
service 'openvpn' do
supports :status => true, :restart => true, :start => true, :stop => true
action :nothing
Chef::Log.info ("\n \t *********Restarting OPEN-VPN*********")
end
由于上述服务是在融合阶段执行的,因此应该已经基于下载的配置文件分配了VPN ip地址。
第3个资源-如果在第2个资源执行时未分配VPN IP,则将请求新的vpn conf文件。
ruby_block 'manual_vpn' do
block do
if node.run_state['error'] == "file not found"
Chef::Log.info ("\n \t ******** VPN IP has not been assigned. File may be corrupted & initiating re-run********")
uri = URI.parse("http://some-url2=#{host}&action=create")
Chef::Log.info (uri)
http = Net::HTTP.new(uri.host,uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
case response.body
when "error"
Chef::Log.info ("\n \t Website reported an Error. Config for the given serial number could have already been generated")
when "Request for vpn successfully added."
Chef::Log.warn ("\n \t **** Inside response processing => Request Accepted **")
Chef::Log.warn ("\n \t *** New vpn config request has been accepted. Waiting for 3 minutes ***")
sleep 180
else
Chef::Log.info ("\n \t Nothing to do - Extra option for future")
end
else
puts "Config file exists and hence not downloading"
end
end
notifies :run, 'ruby_block[re-download-vpn-conf]', :delayed
only_if { Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }.size.eql?(0) }
not_if {node.run_state['error'].eql?("too many redirects")}
end
分配的VPN IP由保护块only_if { Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }.size.eql?(0) }
中的代码检查,并且应该仅在运行时执行。在第二次资源执行结束时,已确定已分配VPN IP,但上述防护中的代码无法检测到它。
我在测试ruby块内的配方末尾使用了Pry调试器,以验证在执行第二个服务重启资源后是否分配了IP。想知道为什么Chef的保护块中的代码无法检测到先前资源执行所分配的VPN。
答案 0 :(得分:0)
基本上,问题是Chef不等待服务后资源执行系统的状态。 Chef不会理会服务是否已启动并且正在运行,它只关心服务的执行。
这是比赛条件的经典示例,厨师动摇头以执行下一个资源。为了避免这种情况,我必须在红宝石块中引入所需的睡眠时间。另一种方法是添加一个until true
条件,但是在我的情况下该方法不起作用,因为服务已启动并正在运行,但厨师中的红宝石块被无限卡住了。
service 'openvpn' do
supports :status => true, :restart => true, :start => true, :stop => true
action :nothing
Chef::Log.info ("\n \t *********Restarting OPEN-VPN*********")
end
ruby_block 'wait for vpn ip' do
block do
Chef::Log.warn ("****************** Sleep block starts ********************")
sleep 30
Chef::Log.warn ("****************** Sleep block exits ********************")
end
action :run
end
ruby_block 'manual_vpn' do
block do
if node.run_state['error'] == "file not found"
Chef::Log.info ("\n \t ******** VPN IP has not been assigned. File may be corrupted & initiating re-run********")
uri = URI.parse("http://some-url2=#{host}&action=create")
Chef::Log.info (uri)
http = Net::HTTP.new(uri.host,uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
case response.body
when "error"
Chef::Log.info ("\n \t Website reported an Error. Config for the given serial number could have already been generated")
when "Request for vpn successfully added."
Chef::Log.warn ("\n \t **** Inside response processing => Request Accepted **")
Chef::Log.warn ("\n \t *** New vpn config request has been accepted. Waiting for 3 minutes ***")
sleep 180
else
Chef::Log.info ("\n \t Nothing to do - Extra option for future")
end
else
puts "Config file exists and hence not downloading"
end
end
notifies :run, 'ruby_block[re-download-vpn-conf]', :delayed
only_if { Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }.size.eql?(0) }
not_if {node.run_state['error'].eql?("too many redirects")}
end