Chef Sugar 旨在提高 Chef 食谱的编写体验

  • João Miranda
  • 孙镜涛

2014 年 1 月 20 日

话题:社区DevOps

Chef SugarChef的一个扩展,它提供了 DSL 方法能够让你编写更加可读的食谱(recipe)。Seth Vargo是 Chef Sugar 的作者,他写出了自己创建 Chef Sugar 的动机并通过示例做了强调。InfoQ 采访了 Seth 以期了解他对语法糖以及 Chef 上下文中插件架构优点的看法。

Chef Sugar 在一些区域提供了 DSL 方法,例如云(例如 Cloud?ec2?)或者平台(例如 ubuntu?centos?)。额外帮助方法的添加正在讨论中。Seth 在自己的文章中展示了一个示例,该示例对使用和没有使用 Chef Sugar 的食谱做了对比。下面这段程序:

include_recipe 'cookbook::_windows_setup' if platform_family?('windows')  
include_recipe 'cookbook::_ec2_setup' if node['ec2'] || node['eucalyptus']

package 'foo' do  
  action :nothing
end.run_action(:install)

execute 'untar package' do  
  if node['kernel']['machine'] == 'x86_64'
    command 'ARCH_FLAGS=x64 make'
  else
    command 'ARCH_FLAGS=i386 make'
  end
  not_if do
    ::File.exists?('/bin/tool') &&
    ::File.executable?('/bin/tool') &&
    shell_out!('/bin/tool --version').stdout.strip == node['tool']['version']
  end
end

credentials = Chef::EncryptedDataBagItem.load('accounts', 'passwords') 

可以被转换成如下的这段:

include_recipe 'cookbook::_windows_setup' if windows?  
include_recipe 'cookbook::_ec2_setup' if ec2? || eucalyptus?

compile_time do  
  package 'apache2'
end

execute 'untar package' do  
  if _64_bit?
    command 'ARCH_FLAGS=x64 make'
  else
    command 'ARCH_FLAGS=i386 make'
  end
  not_if { installed_at_version?('/bin/tool', node['tool']['version']) }
end

credentials = encrypted_data_bag_item('accounts', 'passwords')	

Chef Sugar 是为了在一个食谱中访问而特意编写的,同时它还是一个Chef 类库,你可以将任意的 Ruby 代码包含在食谱中。唯一重要的区别是当它作为一个类库被使用的时候,Chef Sugar 的方法需要一个节点对象(node object)参数。

# cookbook/libraries/default.rb
def only_on_windows(&block)  
  yield if Chef::Sugar::PlatformFamily.windows?(@node)
end 

InfoQ:有些时候语法糖会被看作是一些没有用处、不重要的东西。但是既然你创建了一个名为 Chef Sugar 的项目,那么很显然你并不认为事情总是如此。你认为哪些时候有一点语法糖是好的,哪些时候语法糖有点多?

我认为 Ruby(和 Rails)之所以会在开发人员之间如此成功的首要原因之一就是语法糖。例如,ActiveSupport 向 Ruby 的整数类型添加了语法糖,让它能够执行像 5.days.ago 这样的调用。在降低复杂性和重复性方面语法糖有让人难以置信的用处。许多菜谱(cookbooks)会分享共同的习语或者模式——例如检查是否安装了特定版本的软件。当我看到这种类型的模式出现的时候,我知道是时候去涂上一些糖了。

但是,我并不会积极地寻找领域去“加糖”。我认为这是人们常犯的一个错误。按照我的观点,当逻辑分支或者组件多于两个的时候语法糖就做了过多的事情。例如,开发人员能够很容易地编写一个包装了一个逻辑和资源分支的语法糖,但是最好的方式是将它作为一个类库或者 LWRP 提供。语法糖应该可以很容易地测试和维护,同时新手开发者应该可以仔细分析定义语法糖的方法。

InfoQ:一般来说,这种类型的特性应该存在于一个应用程序的核心中或者最好是将它们作为“插件”,你认为是这样吗?为什么?

我是插件架构模式的忠实拥护者。我能够很容易地将 Chef Sugar 添加到 Chef 核心中,但是我还是有意识地保持它的独立性。就像我们不应该强制一个 Ruby 开发人员使用 ActiveSupport 一样,我们也不能强制一个 Chef 开发者使用 Chef Sugar。

从可维护性的角度来看,将 Chef Sugar 作为一个独立的 gem 能够让我的工作和迭代独立于 Chef 的发布周期。Ruby 本身就采用了这种插件模式,它将核心的 Ruby 类迁移到了 gems。Rubinius 2.0 完全由 gems 组成。就像一个分布式系统相对于一个完全统一的应用程序所具有的优势那样,使用基于插件的模式比将组件绑定到核心框架中具有同样的优势。这种模式也存在于 Vagrant Knife(Chef 的 CLI)及 Rubygems 这样的工具中。

但是,还有一种中庸之道,那就是将插件锁定并捆绑到一个特定版本的核心中(作为一个依赖),即便如此这些插件也是存在于框架的外部作为一个独立的资源。用户可能会在任意时间选择更新到最新版本的插件,也可能会等着升级完整的软件包。Ruby 2.0 就遵循这种模式。如果试图卸载 bigdecimal,那么你将会得到下面的错误:

ERROR:  While executing gem ... (Gem::InstallError)
    gem "bigdecimal" cannot be uninstalled because it is a default gem

但是 Ruby 开发者能够在任意时间选择升级到一个新版本的 bigdecimal。

最后一件要考虑的事情(经常会被我们忽视)是许可的影响。对于 Chef Sugar 而言,Chef 和 Chef Sugar 两者都是基于 Apache 2.0 许可发布的,所以这并不是问题。有些时候我们不能将一个插件捆绑到核心应用程序中,因为它可能并不允许转售,或者涉及到专利等问题。

InfoQ:Chef 中是否还有一些其他的领域可以从附加的 Chef Sugar 方法中受益?

正如我前面所说的,我并不会积极寻找领域去“加糖”。在我第一次发布 Chef Sugar 的时候,它非常受大家欢迎。在短短的 20 分钟之内就有一个社区成员添加了 FreeBSD 平台的支持。然后这个项目度过了两个月没有任何维护的日子(它能“工作”)。大约在一个月之前,有一些人添加了 Cloudstack 支持。

我非常骄傲自己能够快速承认并合并拉请求,所以我一直都鼓励大家提交自己的想法。当我认为某个补丁超出了项目目标的时候,我也不怕对它说不。如果一个社区成员发现了一个应该“加糖”的领域,那么我会非常高兴地去看看在哪!

InfoQ: Chef 中的哪些最重要的部分能够从更多的社区参与中受益?

Chef 的所有问题都能从Chef 软件的报修系统上追踪到。大部分问题都是被分类并按优先级排序的。对于新社区成员而言,被标记为“不重要”或者“微调”的问题可能涉及到最小的变化,同时也是深入 Chef 核心的一个非常好的方式。最后,永远都不要低估印刷或者语法规则修复的能量。因为随着 Chef 变得越来越流行,非英语背景的用户也可能会查阅这些文档。

此外,有一个针对Chef RFC的开源 GitHub 项目。社区也反馈了一些针对 Chef 路线图中的高层特性或者变化的建议。该仓库中的拉请求就极大地受益于社区参与(即使它仅仅是一个简单的:+1:)。

除了 Chef 核心软件项目之外,Chef 社区在去年 11 月份还向COOK 项目(Chef 维护的社区食谱)提交了一些重要的改变。Chef 担负着社区食谱和 Chef 社区成员之间共享的责任。

查看英文原文Chef Sugar Aims to Enhance Chef's Recipes Authoring Experience

社区DevOps