You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:09:35