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

如何为Chef的ruby_block或非模板资源实现Accumulator模式?

Chef累加器自定义资源:非类变量的状态存储方案

可行的状态存储位置

  • node.run_state:Chef Run的临时内存存储,仅当前运行有效,不会写入节点属性,是临时累加数据的首选方案
  • run_context自定义属性:直接在run_context对象上挂载自定义哈希,功能和run_state类似,但run_state更符合官方规范
  • 辅助占位资源:创建无操作的自定义资源,用其属性存储对应file_path的累加数据

按file_path区分的实现方案(基于node.run_state)

最推荐使用node.run_state,可以轻松按file_path分组存储不同的累加数据,修改后的示例代码如下:

action :add do
  # 初始化当前file_path对应的累加容器,避免nil报错
  accumulator_key = "auth_accumulator_#{new_resource.file_path}"
  node.run_state[accumulator_key] ||= {}
  
  # 向对应路径的累加器添加当前实例数据
  node.run_state[accumulator_key][new_resource.host] = {
    'user' => new_resource.user,
    'password' => new_resource.password
  }

  path = new_resource.file_path
  with_run_context :root do
    edit_resource(:ruby_block, "set-auth #{path}") do
      block do
        # 处理文件不存在的边界情况
        json = if ::File.exist?(path)
                 JSON.parse(::IO.read(path))
               else
                 { 'auths' => {} }
               end
        
        # 从run_state取出对应路径的累加数据,写入JSON文件
        json['auths'] = node.run_state[accumulator_key]
        ::IO.write(path, JSON.pretty_generate(json))
      end
      action :run
    end
  end
end

方案说明

  • 用node.run_state["auth_accumulator_#{file_path}"]作为每个文件路径的专属累加容器,确保不同file_path的数据完全隔离
  • 初始化时用||= {}保证首次访问时不会出现NilClass错误
  • ruby_block中直接读取对应路径的累加数据,覆盖或合并到目标JSON结构中
  • 补充了文件不存在的处理逻辑,初始化默认的JSON结构

替代方案(基于run_context自定义存储)

如果不想使用node.run_state,也可以直接在run_context上挂载自定义累加器:

action :add do
  path = new_resource.file_path
  # 初始化run_context的累加器哈希
  unless run_context.instance_variable_defined?(:@auth_accumulators)
    run_context.instance_variable_set(:@auth_accumulators, {})
  end
  accumulators = run_context.instance_variable_get(:@auth_accumulators)
  accumulators[path] ||= {}
  
  # 添加当前实例数据
  accumulators[path][new_resource.host] = {
    'user' => new_resource.user,
    'password' => new_resource.password
  }

  with_run_context :root do
    edit_resource(:ruby_block, "set-auth #{path}") do
      block do
        json = ::File.exist?(path) ? JSON.parse(::IO.read(path)) : { 'auths' => {} }
        json['auths'] = run_context.instance_variable_get(:@auth_accumulators)[path]
        ::IO.write(path, JSON.pretty_generate(json))
      end
      action :run
    end
  end
end

不过这种方式不如node.run_state直观,且run_state是Chef官方推荐的临时存储方式,更符合最佳实践。

内容的提问来源于stack exchange,提问作者Thayne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:22:21