如何在Ruby Block中设置节点级属性并使用延迟求值?
解决Ruby Block设置节点属性后跨资源复用的问题
我之前也踩过Chef阶段执行顺序的坑,你遇到的问题核心在于:Ruby Block是在converge阶段执行的,而普通资源的属性默认会在compile阶段解析——这时候你的node['artifact']['version']还没被JSON文件的值赋值,直接用肯定拿不到正确结果,哪怕用了lazy也可能因为依赖没处理对而失效。下面给你一套可行的解决方案:
1. 正确用Ruby Block读取JSON并设置节点属性
首先确保你的Ruby Block能正确读取JSON并持久化节点属性(用node.normal或node.set,这样属性会保留到节点对象中,后续run也能复用):
ruby_block 'load_artifact_version_from_json' do block do # 替换成你的JSON文件实际路径 json_file_path = '/path/to/your/artifact_info.json' artifact_data = JSON.parse(File.read(json_file_path)) # 用node.normal设置属性,确保属性能被后续资源访问 node.normal['artifact']['version'] = artifact_data['version'] end action :run end
2. 在依赖资源中用lazy求值并设置依赖关系
关键两步:
- 所有需要用到
node['artifact']['version']的资源属性,必须用lazy块包裹,延迟到converge阶段解析 - 给这些资源添加依赖,确保读取JSON的Ruby Block先执行
示例1:在execute资源中生成deploy_art并执行命令
execute 'deploy_war_file' do # 用lazy包裹整个command逻辑,延迟到converge阶段求值 command lazy { deploy_art = "#{node['your_id_key']}-#{node['artifact']['version']}.war" "cp /tmp/#{deploy_art} /opt/tomcat/webapps/" } action :run # 订阅Ruby Block的执行事件,确保先加载属性再执行deploy subscribes :run, 'ruby_block[load_artifact_version_from_json]', :immediately end
示例2:在template资源中传递deploy_art变量
如果是要在模板文件中使用这个值,模板的variables参数也要用lazy:
template '/opt/deploy_script.sh' do source 'deploy_script.sh.erb' # 用lazy包裹variables哈希,延迟解析node属性 variables lazy { { deploy_art: "#{node['your_id_key']}-#{node['artifact']['version']}.war" } } action :create # 同样设置依赖,确保属性已加载 subscribes :create, 'ruby_block[load_artifact_version_from_json]', :immediately end
为什么你之前用lazy没成功?
大概率是这两个原因:
- 没有给依赖资源添加订阅/依赖关系:导致资源在Ruby Block执行前就开始解析属性,
lazy虽然延迟了,但此时属性还没被赋值 lazy的使用位置不对:比如你可能只包裹了部分变量,而不是整个需要延迟求值的属性(比如只包裹了node['artifact']['version'],但整个deploy_art的拼接还是在compile阶段执行了)
额外提示:临时属性用run_state
如果这个版本号只是当前run临时使用,不需要持久化到节点属性中,可以用node.run_state代替node.normal,它只在当前Chef run中有效,不会污染节点属性:
ruby_block 'load_artifact_version' do block do artifact_data = JSON.parse(File.read('/path/to/json')) node.run_state['artifact_version'] = artifact_data['version'] end action :run end # 后续资源中这样用 execute 'deploy' do command lazy { deploy_art = "#{node['your_id_key']}-#{node.run_state['artifact_version']}.war" "cp /tmp/#{deploy_art} /opt/webapps/" } subscribes :run, 'ruby_block[load_artifact_version]', :immediately end
内容的提问来源于stack exchange,提问作者Chucks
相关产品推荐
相关产品推荐

