Chef:强制在模板中使用修改后的属性

Chef: Force use of modified attributes in template

我有菜鸟厨师的问题。我需要更新模板中的目录路径并将其复制到两个不同的地方。尽管模板文件相同,但每个实例的目录路径不同。

我有以下属性文件 (default.rb):

node['conf_dir'] = '/opt/app/etc'     

我有以下模板文件 (myconfig.conf.erb):

ConfigDir = <%= node['conf_dir'] %>

我的食谱文件中有以下内容:

template '/opt/app/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
end

# update conf dir for new app
node['conf_dir'] = '/opt/newapp/etc'
template '/opt/newapp/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
end

我希望在 /opt/app/etc/myconfig.conf 中看到 ConfigDir 的值为“/opt/app/etc”并且我希望在 /opt/newapp/etc/myconfig.conf 中看到 ConfigDir具有“/opt/newapp/etc”的值。当我尝试 运行 配方时,两个应用程序的 configDir 都指向 newapp 目录。为什么我对属性的更新没有生效?厨师可以做这样的事情吗?如果是这样,非常感谢任何指针。谢谢

[不是稳健的解决方案] 在配方 中以相同优先级 定义的属性比属性文件 定义的属性具有更高的优先级(Ohai 的自动属性永远不会更改).您可以查看 attributes precedence in details。在你的情况下,我会使用模板和变量方法,所以你的食谱看起来像:

require 'pp'
node.override['conf_dir'] = '/tmp' 
pp node.debug_value('conf_dir')

template '/opt/app/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  mode '0644'
  variables({   
     :conf_dir => node.default['conf_dir']
  })
end

template '/opt/app/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  mode '0644'
  variables({   
     :conf_dir => node.override['conf_dir']
  })
end

您的属性文件也需要明确指定优先级,因此它将是 node.default['conf_dir'] = '/opt/app/etc' 并且模板文件将使用变量 ConfigDir = <%= @conf_dir %>。在上面的代码片段中,我还留下了 pp node.debug_value('conf_dir') 行,这 非常 对调试很有用。

如果您使用 include_recipe,您可以在 运行 时覆盖属性,但我发现它对您的情况没有用。

这个答案有两个部分,一个是解决原始问题,另一个是针对这个用例提供更好的方法。

1)

因为我非常反对在另一个答案中提到的方式,所以我会利用 lazy evaluation:

写一个

模板:

ConfigDir = <%= @conf_dir %>

食谱:

template '/opt/app/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables( conf_dir: node['conf_dir'] )
end

# update conf dir for new app
ruby_block "update conf dir" do
  block
    node['conf_dir'] = '/opt/newapp/etc'
  end
end

template '/opt/newapp/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
    variables(lazy { conf_dir: node['conf_dir'] } )
end

这样模板的变量实际上是由配方配置的,无论环境、角色或其他配方做什么,因为节点对象的更改是在模板渲染之前完成的,惰性求值与分辨率不同什么值是 node['config'] 直到需要它(在收敛阶段)


2)

旁注:您做事的方式很容易出错,并且会成为维护的噩梦,我建议您回顾一下您做事的方式,例如:

属性文件:

node['apps']['app']['conf_dir'] = '/opt/app/etc'
node['apps']['app']['owner'] = 'root'
node['apps']['app']['group'] = 'root'
node['apps']['newapp']['conf_dir'] = '/opt/newapp/etc'
node['apps']['newapp']['owner'] = 'root'
node['apps']['newapp']['group'] = 'root'

用于定义散列的属性的替代语法:

node['apps']['app'] = { conf_dir: '/opt/app/etc',
                        owner: 'root',
                        group: 'root' 
                      }

模板:

ConfigDir = <%= @conf_dir %>

食谱:

node['apps'].each do |app,properties|
  template "/opt/#{app}/etc/myconfig.conf" do
    source 'myconfig.conf.erb'
    owner properties['owner']
    group properties['group']
    mode '0644'
    variables( conf_dir: properties['conf_dir'] )
  end
end

这样您就不会再在 runtime/compile 时弄乱节点对象,并且您可以轻松添加另一个应用程序而无需再次复制粘贴模板定义。

对于那些想知道双引号和单引号混合的人,变量的插值 #{var} 只发生在双引号内,并且它的用法是在固定字符串上保留单引号以区分它们。

不建议您尝试这样做。如果两个不同的文件需要两个不同的值,那么您真的应该使用两个不同的节点属性。

attributes/default.rb

default['app']['conf_dir'] = '/opt/app/etc'
default['new_app']['conf_dir'] = '/opt/new_app/etc'

模板

ConfigDir = <%= @conf_dir %>

食谱

template '/opt/app/etc/myconfig.conf' do 
  source 'myconfig.conf.erb'
  mode '0644'
  variables({   
     :conf_dir => node['app']['conf_dir']
  })
end

template '/opt/app/etc/myconfig.conf' do
  source 'myconfig.conf.erb'
  mode '0644'
  variables({   
     :conf_dir => node['new_app']['conf_dir']
  })
end