Chef wrapper 食谱最佳实践

Chef wrapper cookbook best practices

在学习 chef 的过程中,我看到了包装食谱的相互矛盾的模式。例如。

一些食谱使用 default.rb,而其他食谱使用 customize.rb 进行覆盖。

attributes/default.rb

attributes/customize.rb 

哪个是最佳做法?

此外,一些包装器食谱在 recipes/default.rb 文件中有这样的参数:

normal['foo']['bar'] = 42

而其他人有

default['foo']['bar'] = 42

还有一些

node.set['foo']['bar'] = 42

此外,一些食谱使用符号和其他字符串

['foo']['bar']
[:foo][:bar]

我应该使用哪种样式?

更新

Chef 似乎发布了官方风格指南,其中至少解决了部分问题(例如符号与字符串)。 https://docs.chef.io/ruby.html#

我还没有看到任何关于如何在包装器说明书中覆盖属性的正式最佳实践,我认为必须选择一种样式,只要您的所有说明书在您的组织内保持一致,您就应该是好的。以下是我个人的看法。


属性文件

我们最初遵循与您描述的 attributes/customize.rb 格式类似的分隔属性文件的风格。但是,我们发现将所有属性保留在 attributes/default.rb 中要简单得多,尤其是因为我们的属性保持在 50 行代码以下。

覆盖优先级

我们总是使用 lowest attribute precedence 在我们的包装器食谱中覆盖属性。我们尽可能坚持 default,因为优先顺序决定了您的食谱的 default 胜过包装的食谱 default.

属性访问方式

您看到的大多数食谱都使用字符串访问格式。

default['foo']['bar'] = 42

事实上有一个foodcritic rule specifically discouraging the use of symbol access. Foodcritic also encourages using a consistent style for accessing attributes。如果您正在决定一种风格,请记住,社区几乎已经决定了字符串访问格式。

不过,还有另一种方法。 Chef 中的属性不是 Hash,它们实际上是 MashMash 恰好支持将属性作为方法访问,所以:

default.foo.bar = 42

我们更喜欢这种语法,因为它更紧凑。

但是,要小心,因为 ,Chef 使用 method_missing 实现了这一点,因此如果属性名称与当前(或未来)相同,您可能 运行 会遇到问题) node 对象方法。我们以我们的组织名称作为所有食谱的序言,因此不太可能发生冲突,但是当我们覆盖社区食谱时,我们可能 运行 出现问题。值得庆幸的是,测试应该可以发现任何此类失误。

注意事项

包装器说明书中的覆盖属性不会以令人惊讶的方式工作,尤其是在涉及字符串插值时。这在 this blog post 中有很好的解释。主要要点是食谱是否将属性定义为:

default['version'] = '1.0'
default['url'] = "http://example.com/#{node['version']}.zip"

并且在你的包装食谱中你覆盖了 version:

default['version'] = '2.0'

default['url'] 仍将解析为 http://example.com/1.0.zip,而不是您预期的 http://example.com/2.0.zip。发生这种情况是因为 url 属性在 发生覆盖之前被插入 。博客 post 进行了更深入的探讨并提供了一个潜在的解决方案。

总结

归根结底,在 Chef 中有多种方法可以做事,而且社区仍在不断成熟。关于编写可重复使用的食谱等有一些最佳实践,但这些实践仍在不断发展。

您能做的最好的事情就是尝试各种风格,找出最适合您和您的团队的风格。一旦你决定了一种风格,我建议你整理一份风格指南(我用过 this Angular style guide as inspiration). You can also enforce your style guide with custom foodcritic rules。我们用这种方法取得了成功。

可悲的是,最佳实践很少,但有一些问题需要注意。

属性加载顺序

属性文件以非常具体但不是很清楚的顺序加载(执行)。

  1. 所需的说明书根据运行列表和运行列表中引用的每个说明书的元数据以及所有依赖项解析。
  2. 编译完相关食谱列表后,它们将按字典顺序排列。
  3. 每个食谱中的属性文件然后按字典顺序排列。
  4. 执行从列表的开头开始,但是,如果遇到 include_attribute 语句,将立即执行该属性文件(并从列表中删除,以免再次执行。 )

这很重要,因为如果同一属性设置在同一级别,即 default 由多个属性文件设置,那么最后执行的文件将决定哪个值获胜。 __So 总是 include_attribute 包装器食谱的属性文件顶部的库食谱中的属性文件。然后在 default 级别设置属性(以便它们可以更容易地在其他地方被覆盖)。

其中一个与其他不同

普通属性(node.set 或 node.normal)是一个有趣的野兽。他们在厨师运行之间坚持。因此,一旦您将某个属性设置为正常,它就会一直存在,直到您将其删除或更改为止。有时这是一件好事,就像你想要记住的 uuid,但有时这是一个意想不到的副作用。

风格

符号和字符串,还有方法,我的天啊。

关于 node.attributenode[:attribute]node['attribute'] 访问方式有很多争论。如果您想看到硬币的两面,只需阅读美食评论家 001。在我看来,node['attribute'] 赢得了社区食谱开发者的大多数,但只是微乎其微的多数。 node.attribute 几乎普遍不被接受,因为它会产生不良的副作用。最终,你会使用你最喜欢的东西,但如果你的图书馆食谱相当一致,我建议你遵循他们的惯例。

覆盖哪里

就个人而言,我尽量不在属性文件之外设置属性。它经常导致头痛。将所有属性集操作保存在属性文件中会更容易,因此您可以轻松地找到它们。但有时您不能在属性文件中这样做,或者在配方中这样做更有意义。与大多数编程规则一样,您只需要权衡与感觉正确的一致性。