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

请求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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:33:21