我们的存储库中的两个不同文件包含以下行:
# library_cookbook_1/attributes/default.rb
default[:library_cookbook_1][:foo] = true
# library_cookbook_2/attributes/default.rb
default[:library_cookbook_2][:foo] = true
我想合并两个属性。仅仅设置一个等于另一个是不够的,因为它们都可以互换使用。我最担心的是有人犯了这个错误:
"environment_json_attributes": {
"library_cookbook_1": {
"foo": false //Now it's false half of the time...
}
}
忘记在我们的代码中设置两个变量的值是我们当前结构下的一个致命错误。我希望以维护覆盖层次结构的方式合并属性,这样如果library_cookbook_1具有角色级别覆盖,但library_cookbook_2具有环境级别覆盖,则chef将处理覆盖,就好像属性只是一个属性一样。
理想的合并代码看起来像这样:
> node.attributes.debug_value('library_cookbook_1', 'foo')
{'precedence1' => true, 'precendence3' => false}
> node.attributes.debug_value('library_cookbook_2', 'foo')
{'precedence1' => false, 'precendence2' => true}
> attr_merge(['library_cookbook_1', 'foo'], ['library_cookbook_2', 'foo'])
> node.attributes.debug_value('library_cookbook_1', 'foo')
{'precedence1' => true, 'precendence2' => true, 'precendence3' => false}
> node.attributes.debug_value('library_cookbook_2', 'foo')
{'precedence1' => true, 'precendence2' => true, 'precendence3' => false}
> node[:library_cookbook_1][:foo].precedence4 = true
> node.attributes.debug_value('library_cookbook_2', 'foo')
{'precedence1' => true, 'precendence2' => true, 'precendence3' => false, 'precedence4' => true}
理想情况下,我希望每个属性都是指向同一个东西的指针,这样,未来的调用和分配都适用于这两个属性。
现在,显而易见的答案是简单地重构其中一个属性。不幸的是,厨师使重构属性变得困难 - 我们有10个不同的环境,7个不同的角色,每个环境20个节点等等。如果我们试图重构整个事物,我们肯定会犯一个以微妙方式出现的错误。
答案 0 :(得分:1)
重构使用属性的cookbook来改为使用单个属性。然后添加一些代码,如果原始attrs中的任何一个被设置为nil(或sentinel)以外的其他东西,它将中止Chef运行。在第一个小时左右可能会有点烦人,但你会很快找到旧值的设定位置。
如果做错了,请制作一个采用单一属性的食谱,并根据它设置两个旧值的force_override。这意味着如果存在冲突,新值总是会赢。
答案 1 :(得分:0)
如果我们试图重构整个事情,我们肯定会犯一个以微妙方式浮出水面的错误。
你需要先解决这个问题!您需要有足够的自动化测试才能对变化充满信心。如果您担心进行更改,那么您将陷入困境,永远无法与Chef进行任何关系。
使用ChefSpec快速单元测试您的食谱代码(不应用任何更改)
也许您需要像Vagrant或chefdev
环境一样的虚拟机来在您的真实环境之前使用新的烹饪书测试完全融合的节点。
作为旁注,在多环境设置中,您可能也需要stay away from roles。
显而易见的答案是简单地重构其中一个属性
无论您选择何种方法,您都需要重构,因为Chef并不支持您想要的开箱即用。添加非显而易见的kludges以防止改变事物只会在以后咬你。
所以,你明显的答案将是我的答案......
重构cookbook以使用单个属性。单个属性已经很好地precedence了。