请求Ruby语法帮助:在Chef的ruby_block中嵌套template块
解决Chef中Ruby块嵌套Template资源的语法问题
首先得给你划个重点:Chef的资源(比如template、service)不能直接写在ruby_block的block do...end代码块里,因为ruby_block是用来跑纯Ruby逻辑的,而Chef资源的声明属于资源集合的构建操作,得通过Chef的run_context来管理才行。下面给你具体的正确实现方式:
正确代码示例
ruby_block "check_nginx_port" do block do server = node['fqdn'] # 先修正你代码里的变量赋值错误,这里假设端口来自节点属性,你可以根据实际调整 port = node['nginx']['global_ssl_port'].to_i port_open = false begin Timeout.timeout(5) do # 尝试连接端口,判断是否处于监听状态 Socket.tcp(server, port, connect_timeout: 5) { port_open = true } end rescue Errno::ECONNREFUSED, Timeout::Error port_open = false end # 如果端口没在监听,就把template和service资源加入Chef的执行队列 unless port_open # 创建并配置template资源,用来修改NGINX配置 template_resource = Chef::Resource::Template.new('/etc/nginx/nginx.conf', run_context) template_resource.source('nginx.conf.erb') template_resource.cookbook('your_cookbook_name') # 替换成你的cookbook名称 template_resource.owner('root') template_resource.group('root') template_resource.mode('0644') template_resource.variables(ssl_port: port) # 传递端口变量给模板 # 将资源加入Chef的资源集合 run_context.resource_collection.insert(template_resource) # 创建并配置service资源,用来重启NGINX service_resource = Chef::Resource::Service.new('nginx', run_context) service_resource.action(:restart) # 设置依赖:配置更新后立即重启服务 service_resource.subscribes(:restart, "template[/etc/nginx/nginx.conf]", :immediately) run_context.resource_collection.insert(service_resource) end end action :run end
关键细节说明
- 别直接在ruby_block里写资源:Chef的资源声明不是普通Ruby代码,得先通过
Chef::Resource::XXX.new创建资源对象,再用run_context.resource_collection.insert把它加到Chef的执行队列里。 - 变量赋值修正:你原来代码里
port = puts global_ssl_port.to_i是错的,puts会输出内容并返回nil,应该直接给端口变量赋值(比如从节点属性或者自定义变量获取)。 - 异常处理:捕获
ECONNREFUSED(连接被拒绝)和Timeout::Error(超时)这两种异常,才能准确判断端口是否未监听。 - 资源依赖控制:通过
subscribes让服务重启动作依赖于配置文件的更新,保证修改后的配置能立即生效。
更优雅的替代方案(推荐)
其实没必要用ruby_block嵌套资源,用Chef的guard条件来控制资源执行会更简洁,也符合Chef声明式的风格:
# 定义template资源,只有当端口未监听时才执行更新 template '/etc/nginx/nginx.conf' do source 'nginx.conf.erb' cookbook 'your_cookbook_name' owner 'root' group 'root' mode '0644' variables ssl_port: node['nginx']['global_ssl_port'].to_i # guard条件:端口监听时不执行,未监听时执行 not_if do server = node['fqdn'] port = node['nginx']['global_ssl_port'].to_i begin Timeout.timeout(5) { Socket.tcp(server, port, connect_timeout: 5) } true # 端口正常监听,跳过template更新 rescue Errno::ECONNREFUSED, Timeout::Error false # 端口未监听,执行template更新 end end # 配置更新后立即重启NGINX notifies :restart, 'service[nginx]', :immediately end # 定义service资源,默认不执行动作,等待template的触发 service 'nginx' do action :nothing end
这种写法逻辑更清晰,也更容易维护,推荐你优先使用这种方式。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

