使用 Serverspec 进行 Docker 图像构建测试检查空的 gems 缓存失败

Checking on empty gems cache fails using Serverspec for Docker image build testing

我目前在使用 Serverspec 测试 Docker 图像构建时遇到问题。简而言之,我想要做的是确保在图像构建期间明确清除 Ruby gems 构建缓存,例如通过在 Docker 文件中发出 rm -rf /usr/lib/ruby/gems/*/cache/*.gem

我正在使用的 Docker 文件框架如下所示:

 # Dockerfile

 FROM alpine:3.7

 RUN apk add --no-cache \ 
     dumb-init \
     ruby \
  && apk add --no-cache --virtual .build-deps \
     build-base \
     ruby-dev

 RUN gem install --no-rdoc --no-ri json \
  && gem install --no-rdoc --no-ri oj

 RUN apk del .build-deps \
  && rm -rf /var/cache/apk/* \ 
     /tmp/* /var/tmp/*

在添加 gem 缓存删除命令之前,我实现了以下 Serverspec 测试以便能够从失败的测试开始:

 # ./spec/Dockerfile_spec.rb

 describe "Dockerfile" do
   before(:all) do
     @image = Docker::Image.build_from_dir('.')
     @image.tag(repo: 'demo', tag: 'latest')

     set :os, family: :alpine
     set :backend, :docker
     set :docker_image, @image.id
   end


   it "removes build dependencies during cleanup" do
     # Some more assertions 
     # ...
     expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to eq 0  
   end
 end

假设上面的 Docker 文件,测试 运行 绿色:

 $ bundle exec rspec spec/Dockerfile_spec.rb
 ....
 Finished in 3.95 seconds (files took 0.24091 seconds to load)
 4 examples, 0 failures

然而,这不应该是这种情况,因为 gem 缓存尚未被清除,因此不是空的,即我希望相应的断言在测试执行期间失败。

这可以通过从新构建的映像启动容器并检查 gems 缓存目录来轻松验证:

 $ docker run --rm -it demo:latest sh
 / # ls /usr/lib/ruby/gems/2.4.0/cache/ 
 json-2.1.0.gem  oj-3.4.0.gem

反方向测试并期望一个非空目录,执行失败并显示错误消息:

 # ./spec/Dockerfile.rb

 it "removes build dependencies during cleanup" do
   # Some more assertions 
   # ...
   expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0  
 end


 # Command line 

 $ bundle exec rspec spec/Dockerfile_spec.rb
 .F..

 Failures:

   1) Dockerfile removes build dependencies during cleanup
      Failure/Error: expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0

      expected: value != 0
           got: 0

      (compared using ==)

很明显,Dir.glob() 命令没有 return gem 缓存目录中的正确文件数。

有趣的是,运行在容器内手动执行 Dir.glob() 命令 returns 预期结果:

 $ docker run --rm -it demo:latest sh
 / # apk add --no-cache ruby-irb
 ...
 / # irb
 irb(main):001:0> Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size
 => 2

首先,这让我认为测试没有在容器内正确执行,而是在主机上执行,但进一步的实验无法证实这一点。

你有什么想法吗?这可能是 Serverspec/Rspec 问题吗?

谢谢!

编辑

First, this made me think that the test is not properly executed within the container but on the host instead, but further experiments could not confirm this.

我终于发现这个假设是错误的。实际上,无论出于何种原因, Dir.glob() 调用都是在容器外部执行的。

我终于明白是哪里出了问题,其实很简单:

假设我们有一个这样的 Serverspec 测试用例:

it "removes build dependencies during cleanup" do
  # Some more assertions 
  # ...
  expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0  
end

现在在执行期间发生的事情是 Dir.glob() 调用在调用 expect() 例程之前被评估。因此,正如我在 post 中指出的那样,Dir.glob() 命令在容器范围之外 运行,而是检查相应的主机目录。 如果您想检查容器文件夹是否为空,可以按如下方式实现:

it "clears gem/apk caches as well as tmp files/dirs" do
  expect(command('ls /usr/lib/ruby/gems/*/cache/*.gem | wc -l').stdout).to eq "0\n"
end

诚然,这可能不是最漂亮、最优雅的解决方案,但基本上可以完成工作。如果您有其他想法和建议,请随时 post 提出。