5

我已经建立了一本安装 Jenkins CI 的食谱。它使用食谱中的keyrepository资源yum,所以我最终得到以下食谱:

yum_key "RPM-GPG-KEY-jenkins" do
  url "http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key"
  action :add
end

yum_repository "jenkins" do
  description "Jenkins-CI 3rd party repository"
  url "http://pkg.jenkins-ci.org/redhat"
  key "RPM-GPG-KEY-jenkins"
  action :add
end

当我将此食谱包含在另一个食谱中时:

include_recipe 'sp_jenkins::default'

我使用以下 ChefSpec 测试对此进行了测试

it 'includes the `sp_jenkins::default` recipe' do
  expect(chef_run).to include_recipe('sp_jenkins::install')
end

我的 ChefSpec 测试失败,输出如下:

NameError:
  Cannot find a resource for yum_key on chefspec version 0.6.1

(我不确定为什么它说版本 0.6.1,gem list告诉我它使用的是 3.0.2)

sp_jenkins食谱确实依赖于yum食谱(metadata.rb),并且运行良好,但是,我目前正在编写的食谱不依赖于食谱yum,因此没有可用的yum_keyandyum_repository方法。

有没有办法防止 ChefSpec “下降”到包含的食谱/食谱中,而只是测试当前的食谱?

4

3 回答 3

3

哦哈!Julian 是正确的——ChefSpec 实际上在本地机器的内存中运行Chef Solo 。它将提供程序操作重写为 noop,但创建所有已执行操作的注册表(包括执行通知时将执行的操作)。

因此,就像您需要yum食谱将这个配方收敛到真实节点上一样,您需要在使用 ChefSpec 进行单元测试期间将其收敛。最简单的方法是使用 Berkshelf 或 Librarian 解析器。要使用Berkshelf 解析器,只需require 'chefspec/berkshelf'在要求之后chefspec

# spec_helper.rb
require 'chefspec'
require 'chefspec/berkshelf'

如果您的系统上安装了 Berkshelf,它会将所有食谱拉入一个临时目录并为您运行 ChefSpec。

您可能还想看看Strainer,它旨在解决类似的问题。


在一个有点不相关的注释中,我正在对Jenkins 食谱进行相当大的重构,它可能更适合您的需求。


资料来源:

  • 我写的...
于 2013-12-14T20:34:44.710 回答
2

不,没有办法阻止它下降,因为它试图在内存中聚合整个 Chef 运行。

但是,如果您在 ChefSpec 中使用 Berkshelf 功能,Berkshelf 依赖解析器会将所有依赖的说明书提供给内存中的 Chef 运行,您将获得成功。

于 2013-12-13T18:12:07.657 回答
0

期望单独测试你的说明书是绝对有效的而不是将其他项目的代码包含在你的测试范围内。不幸的是,我找不到支持的“干净”方式来做到这一点。我能够做到这一点,但这是有代价的。

要使用这种技术,不要require 'chefspec/berkshelf'在测试代码中的任何地方,只在chefspec它本身,因为你故意收集其他食谱源。这是我的工作测试模块的模板(不是我的完整测试代码,因为我省略了 RSpec 配置选项):

describe 'mycookbook::recipe' do
  let(:chef_run) do
    ChefSpec::SoloRunner.new(platform: 'x', version: 'x') {
      # ...
    }.converge(described_recipe)
  end

  before :each do
    allow_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:cookbook_order) do
      Chef::Log.debug 'Attempt to source external cookbooks blocked'
      [described_cookbook]
    end
    allow_any_instance_of(Chef::Recipe).to receive(:include_recipe) do |recipe|
      Chef::Log.debug "Attempt to include #{recipe} blocked"
    end
  end

  it 'works' do
    # ...
  end
end

您的before. 我必须为该:cookbook_order方法的拦截工作。我不得不深入研究 Chef 内部结构才能发现这一点。请记住,这对我使用 Chef 14 有效,但不能保证这在未来是安全的。CookbookCompiler升级 Chef 后,如果实施发生变化,您可能必须找到另一个解决方案。(Chef::Recipe.include_recipe然而,拦截是受支持的 API,因此应该至少在某种程度上是未来安全的。)

而且,我提到这是有代价的。(除了使用不受支持的破解!)您将无法expect为您的食谱或属性包括任何 s,除非在您自己的食谱中。像这样的测试用例将失败,因为实际上无法包含配方,因为您正在阻止:

it 'includes othercookbook::recipe' do
  expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('othercookbook::recipe')
end

此外,您现在必须在您的before块中满足所有属性和其他先决条件,否则这些属性和其他先决条件可能会被您的运行列表中的其他配方满足。因此,您这样做可能会让自己承受相当大的痛苦。但是,一旦你完成了,你将有更少的脆弱测试。(虽然外部依赖要做到100%纯洁,但也必须投降fauxhai,这样会更痛苦。)

于 2018-07-31T21:08:00.343 回答